Java——十进制格式。解析以指定的小数位数返回双值。

时间:2021-10-07 17:10:37

I want to be able to convert a string to a Double given a number of decimal places in a format string. So "###,##0.000" should give me a Double to 3 decimal places.

我希望能够将一个字符串转换为一个双给定的格式字符串中的小数位数。所以### ### #0.000应该给我一个2到3位小数。

Edit - added more info to what happens

编辑-增加更多的信息发生

The user enters the value in the UI - which is input into a String. The rule is this value is limited to 3 decimal places. The underlying code stores the value in the database which is then used in a calculation. Therefore the trailing decimal places will cause the calculations to be out slightly to what would be expected.

用户在UI中输入值——它被输入到一个字符串中。规则是这个值被限制为3位小数。底层代码将值存储在数据库中,然后在计算中使用。因此,后面的小数部分将使计算稍微偏离预期。

I have the following code:

我有以下代码:

try {
        // output current locale we are running under (this happens to be "nl_BE")
        System.out.println( "Current Locale is " + Locale.getDefault().toString() );

        // number in Central European Format with a format string specified in UK format
        String numberCE = "1,234567"; // 1.234567
        String formatUK = "###,##0.000";

        // do the format
        DecimalFormat formatterUK = new DecimalFormat( formatUK );
        Double valCEWithUKFormat = formatterUK.parse( numberCE ).doubleValue();

        // I want the number to DPs in the format string!!!
        System.out.println( "CE Value     " + numberCE + " in UK format (" + formatUK + ") is "
        + valCEWithUKFormat );

    } catch( ParseException ex ) {
        System.out.println("Cannot parse number");
    }
}

The DecimalFormat seems to ignore the format string and gives me the complete string as a Double of 1.234567.

DecimalFormat似乎忽略了格式字符串,并将完整的字符串作为1.234567的双精度字符提供给我。

Can DecimalFormat be forced to use the format string when parsing? Am I missing something?

在解析时,DecimalFormat会被强制使用格式字符串吗?我遗漏了什么东西?

Cheers,

欢呼,

Andez

Andez

4 个解决方案

#1


16  

DecimalFormat is used for two distinct purposes: parsing input and formatting output. If you want to do both, you'll have to use the format object twice.

DecimalFormat用于两个不同的目的:解析输入和格式化输出。如果您想同时使用这两种格式,那么您必须使用format对象两次。

If you want to take that value and format the output, restricting the number of significant digits, you need to use the format object again. This time it uses your formatting rules to create an output string from a numeric value:

如果您想获取该值并格式化输出,限制有效位数的数量,则需要再次使用format对象。这一次,它使用格式化规则从数值创建输出字符串:

String output = formatterUK.format(valCEWithUKFormat.doubleValue() );

This will give you the output of 1,235

结果是1235

It seems you want this numeric value to be presented in the 1.235 format. To do this, you should format the output using a specific locale (if yours uses a different format).

看起来您希望这个数值以1.235格式显示。为此,您应该使用特定的语言环境(如果您的语言环境使用不同的格式)对输出进行格式化。

HOWEVER, I would recommend approaching this problem differently:

但是,我建议以不同的方式处理这个问题:

String text = "1,234567";
NumberFormat nf_in = NumberFormat.getNumberInstance(Locale.GERMANY);
double val = nf_in.parse(text).doubleValue();

NumberFormat nf_out = NumberFormat.getNumberInstance(Locale.UK);
nf_out.setMaximumFractionDigits(3);
String output = nf_out.format(val);

A few notes:

一些笔记:

  • Input parsing should be kept separate from output formatting. Especially when you start throwing in multiple Locales.
  • 输入解析应该与输出格式化分开。特别是当您开始在多个地区抛出。
  • Allow the standard library to do the heavy lifting for determining what a valid input value is for a given Locale. You just need to select an appropriate Locale (I chose GERMANY, but this would obviously work with others). Always use Locales when possible. Don't try to recreate formatting strings.
  • 允许标准库执行繁重的任务,以确定给定地区的有效输入值。您只需选择适当的语言环境(我选择了德国,但这显然适用于其他国家)。尽可能使用locale。不要尝试重新创建格式化字符串。
  • Always store your input value SEPARATE from any output formatting. IE, if you want to show only three digits in the output, that's fine, but store the whole double value anyway.
  • 始终将输入值与任何输出格式分开存储。如果你想在输出中只显示三个数字,那没问题,但是无论如何都要存储整个double值。

#2


5  

Taking on board what you said I have modified my code slightly to cover different locales. The key was taking a value string in a localised format to a Double that is rounded based on the format string.

考虑到你所说的,我稍微修改了我的代码以覆盖不同的地区。关键是将一个本地化格式的值字符串转换为双精度字符,并根据格式字符串进行四舍五入。

The format string is always a UK based format with the decimal seperators specified as "." and thousand seperators specified as ",".

格式字符串始终是基于英国的格式,十进制分隔符指定为“.”,一千个分隔符指定为“,”。

I am using the DecimalFormat to initially parse the localised format based on a specified locale. This gives a Double equivalent of the string correctly. I then use a BigDecimal to handle the rounding. I can get the number of decimal places from the DecimalFormat instance and call setScale on the BigDecimal to perform the rounding.

我正在使用DecimalFormat最初根据指定的语言环境解析本地化的格式。这就给出了正确的双等价字符串。然后我使用一个BigDecimal来处理四舍五入。我可以从DecimalFormat实例获得小数点的位数,并在BigDecimal上调用setScale来执行舍入。

The initial code structure has been modified to allow you to see what happens under different locale circumstances thanks @RD01 for noting importance of other locales.

由于@RD01注意到了其他地区的重要性,最初的代码结构已经被修改,允许您查看在不同的地区环境下发生了什么。

I now have code as follows:

我现在的代码如下:

private void runTests3() {
    // output current locale we are running under
    System.out.println( "Current Locale is " + Locale.getDefault().toString() );

    // number in Central European Format with a format string specified in UK format
    String numbersInEuropeanFormatString[] = new String[] { "1.000,234567", "1,2345678", "1.222.333,234567" };
    String formatUK = "###,##0.0000";

    // output numbers using the german locale
    System.out.println("Output numbers using the German locale\n");
    for(String num : numbersInEuropeanFormatString ) {
        formatNumberAsDouble(num, formatUK, Locale.GERMAN);
    }

    // output numbers using the UK locale.  
    // this should return unexpected results as the number is in European format
    System.out.println("Output numbers using the UK locale\n");
    for(String num : numbersInEuropeanFormatString ) {
        formatNumberAsDouble(num, formatUK, Locale.UK);
    }

    // output numbers using new DecimalFormat( formatUK ) - no locale specified
    System.out.println("\n\nOutput numbers using new DecimalFormat( " + formatUK + " )\n");
    for(String num : numbersInEuropeanFormatString ) {
        formatNumberAsDouble( num, formatUK, null);
    }
}

private void formatNumberAsDouble(String value, String format, Locale locale) {


    NumberFormat formatter;
    int decimalPlaces;

    // create the formatter based on the specified locale
    if( locale != null ) {
         formatter = NumberFormat.getNumberInstance(locale);
         // creating the above number format does not take in the format string
         // so create a new one that we won't use at all just to get the
         // decimal places in it
         decimalPlaces = (new DecimalFormat(format)).getMaximumFractionDigits();
    } else {
        formatter = new DecimalFormat( format );
        decimalPlaces = formatter.getMaximumFractionDigits();
    }

    // get the result as number
    Double result = null;
    try {
        result = formatter.parse( value ).doubleValue();
    } catch( ParseException ex ) {
        // not bothered at minute
    }

    // round the Double to the precision specified in the format string


    BigDecimal bd = new BigDecimal(result );
    Double roundedValue = bd.setScale( decimalPlaces, RoundingMode.HALF_UP ).doubleValue();

    // output summary
    System.out.println("\tValue = " + value);
    System.out.println( locale == null  ? "\tLocale not specified" : "\tLocale = " + locale.toString());
    System.out.println( format == null || format.length() == 0 ? "\tFormat = Not specified" : "\tFormat = " + format);
    System.out.println("\tResult (Double) = " + result);
    System.out.println("\tRounded Result (Double) (" + decimalPlaces + "dp) = " + roundedValue);
    System.out.println("");
}

This produces the following output:

这将产生以下输出:

Current Locale is nl_BE
Output numbers using the German locale

    Value = 1.000,234567
    Locale = de
    Format = ###,##0.0000
    Result (Double) = 1000.234567
    Rounded Result (Double) (4dp) = 1000.2346

    Value = 1,2345678
    Locale = de
    Format = ###,##0.0000
    Result (Double) = 1.2345678
    Rounded Result (Double) (4dp) = 1.2346

    Value = 1.222.333,234567
    Locale = de
    Format = ###,##0.0000
    Result (Double) = 1222333.234567
    Rounded Result (Double) (4dp) = 1222333.2346

Output numbers using the UK locale

    Value = 1.000,234567
    Locale = en_GB
    Format = ###,##0.0000
    Result (Double) = 1.0
    Rounded Result (Double) (4dp) = 1.0

    Value = 1,2345678
    Locale = en_GB
    Format = ###,##0.0000
    Result (Double) = 1.2345678E7
    Rounded Result (Double) (4dp) = 1.2345678E7

    Value = 1.222.333,234567
    Locale = en_GB
    Format = ###,##0.0000
    Result (Double) = 1.222
    Rounded Result (Double) (4dp) = 1.222



Output numbers using new DecimalFormat( ###,##0.0000 )

    Value = 1.000,234567
    Locale not specified
    Format = ###,##0.0000
    Result (Double) = 1000.234567
    Rounded Result (Double) (4dp) = 1000.2346

    Value = 1,2345678
    Locale not specified
    Format = ###,##0.0000
    Result (Double) = 1.2345678
    Rounded Result (Double) (4dp) = 1.2346

    Value = 1.222.333,234567
    Locale not specified
    Format = ###,##0.0000
    Result (Double) = 1222333.234567
    Rounded Result (Double) (4dp) = 1222333.2346

#3


0  

Sure you can. Try running this:

你当然可以。尝试运行:

String in = "1,234567";
System.out.println(NumberFormat.getNumberFormat(new Locale("fr", "FR")).parse(in));
System.out.println(NumberFormat.getNumberFormat(new Locale("en", "GB")).parse(in));

Clearly they result in different output, the first reading 1.234567 and the second 1234567. Maybe there's something wrong with your pattern? Anyway the last line there would be the preferred way of getting the UK standard format.

显然,它们导致不同的输出,第一个读数为1.234567,第二个读数为1234567。也许你的模式有问题?不管怎样,最后一行是获得英国标准格式的首选方法。

#4


0  

The restriction of decimal places in DecimalFormat is really meant for use in the format() method and doesn't have much effect in the parse() method.

decimal格式中的小数点限制实际上是用于format()方法,在parse()方法中没有太大影响。

In order to get what you want you need this:

为了得到你想要的,你需要:

    try {
        // output current locale we are running under (this happens to be "nl_BE")
        System.out.println("Current Locale is " + Locale.getDefault().toString());

        // number in Central European Format with a format string specified in UK format
        String numberCE = "1,234567"; // 1.234567
        String formatUK = "###,##0.000";

        // do the format
        DecimalFormat formatterUK = new DecimalFormat(formatUK);
        Double valCEWithUKFormat = formatterUK.parse(numberCE).doubleValue();

        // first convert to UK format string
        String numberUK = formatterUK.format(valCEWithUKFormat);
        // now parse that string to a double
        valCEWithUKFormat = formatterUK.parse(numberUK).doubleValue();

        // I want the number to DPs in the format string!!!
        System.out.println("CE Value     " + numberCE + " in UK format (" + formatUK + ") is " + valCEWithUKFormat);

    } catch (ParseException ex) {
        System.out.println("Cannot parse number");
    }

You first need to get the number as a UK format string and then parse that number, using the UK formatter. That will get you the result you're looking for. NB, this will round the number to 3 decimal places, not truncate.

首先需要将数字作为UK格式字符串获取,然后使用UK格式化程序解析该数字。这会得到你想要的结果。NB,这将把数字四舍五入到小数点后三位,而不是截断。

By the way, I'm slightly surprised that your UK formatter is able to parse the CE format number. You really should be parsing the original number with a CE format parser.

顺便说一下,我对您的UK格式化程序能够解析CE格式号感到有点惊讶。您确实应该使用CE格式解析器解析原始数字。

#1


16  

DecimalFormat is used for two distinct purposes: parsing input and formatting output. If you want to do both, you'll have to use the format object twice.

DecimalFormat用于两个不同的目的:解析输入和格式化输出。如果您想同时使用这两种格式,那么您必须使用format对象两次。

If you want to take that value and format the output, restricting the number of significant digits, you need to use the format object again. This time it uses your formatting rules to create an output string from a numeric value:

如果您想获取该值并格式化输出,限制有效位数的数量,则需要再次使用format对象。这一次,它使用格式化规则从数值创建输出字符串:

String output = formatterUK.format(valCEWithUKFormat.doubleValue() );

This will give you the output of 1,235

结果是1235

It seems you want this numeric value to be presented in the 1.235 format. To do this, you should format the output using a specific locale (if yours uses a different format).

看起来您希望这个数值以1.235格式显示。为此,您应该使用特定的语言环境(如果您的语言环境使用不同的格式)对输出进行格式化。

HOWEVER, I would recommend approaching this problem differently:

但是,我建议以不同的方式处理这个问题:

String text = "1,234567";
NumberFormat nf_in = NumberFormat.getNumberInstance(Locale.GERMANY);
double val = nf_in.parse(text).doubleValue();

NumberFormat nf_out = NumberFormat.getNumberInstance(Locale.UK);
nf_out.setMaximumFractionDigits(3);
String output = nf_out.format(val);

A few notes:

一些笔记:

  • Input parsing should be kept separate from output formatting. Especially when you start throwing in multiple Locales.
  • 输入解析应该与输出格式化分开。特别是当您开始在多个地区抛出。
  • Allow the standard library to do the heavy lifting for determining what a valid input value is for a given Locale. You just need to select an appropriate Locale (I chose GERMANY, but this would obviously work with others). Always use Locales when possible. Don't try to recreate formatting strings.
  • 允许标准库执行繁重的任务,以确定给定地区的有效输入值。您只需选择适当的语言环境(我选择了德国,但这显然适用于其他国家)。尽可能使用locale。不要尝试重新创建格式化字符串。
  • Always store your input value SEPARATE from any output formatting. IE, if you want to show only three digits in the output, that's fine, but store the whole double value anyway.
  • 始终将输入值与任何输出格式分开存储。如果你想在输出中只显示三个数字,那没问题,但是无论如何都要存储整个double值。

#2


5  

Taking on board what you said I have modified my code slightly to cover different locales. The key was taking a value string in a localised format to a Double that is rounded based on the format string.

考虑到你所说的,我稍微修改了我的代码以覆盖不同的地区。关键是将一个本地化格式的值字符串转换为双精度字符,并根据格式字符串进行四舍五入。

The format string is always a UK based format with the decimal seperators specified as "." and thousand seperators specified as ",".

格式字符串始终是基于英国的格式,十进制分隔符指定为“.”,一千个分隔符指定为“,”。

I am using the DecimalFormat to initially parse the localised format based on a specified locale. This gives a Double equivalent of the string correctly. I then use a BigDecimal to handle the rounding. I can get the number of decimal places from the DecimalFormat instance and call setScale on the BigDecimal to perform the rounding.

我正在使用DecimalFormat最初根据指定的语言环境解析本地化的格式。这就给出了正确的双等价字符串。然后我使用一个BigDecimal来处理四舍五入。我可以从DecimalFormat实例获得小数点的位数,并在BigDecimal上调用setScale来执行舍入。

The initial code structure has been modified to allow you to see what happens under different locale circumstances thanks @RD01 for noting importance of other locales.

由于@RD01注意到了其他地区的重要性,最初的代码结构已经被修改,允许您查看在不同的地区环境下发生了什么。

I now have code as follows:

我现在的代码如下:

private void runTests3() {
    // output current locale we are running under
    System.out.println( "Current Locale is " + Locale.getDefault().toString() );

    // number in Central European Format with a format string specified in UK format
    String numbersInEuropeanFormatString[] = new String[] { "1.000,234567", "1,2345678", "1.222.333,234567" };
    String formatUK = "###,##0.0000";

    // output numbers using the german locale
    System.out.println("Output numbers using the German locale\n");
    for(String num : numbersInEuropeanFormatString ) {
        formatNumberAsDouble(num, formatUK, Locale.GERMAN);
    }

    // output numbers using the UK locale.  
    // this should return unexpected results as the number is in European format
    System.out.println("Output numbers using the UK locale\n");
    for(String num : numbersInEuropeanFormatString ) {
        formatNumberAsDouble(num, formatUK, Locale.UK);
    }

    // output numbers using new DecimalFormat( formatUK ) - no locale specified
    System.out.println("\n\nOutput numbers using new DecimalFormat( " + formatUK + " )\n");
    for(String num : numbersInEuropeanFormatString ) {
        formatNumberAsDouble( num, formatUK, null);
    }
}

private void formatNumberAsDouble(String value, String format, Locale locale) {


    NumberFormat formatter;
    int decimalPlaces;

    // create the formatter based on the specified locale
    if( locale != null ) {
         formatter = NumberFormat.getNumberInstance(locale);
         // creating the above number format does not take in the format string
         // so create a new one that we won't use at all just to get the
         // decimal places in it
         decimalPlaces = (new DecimalFormat(format)).getMaximumFractionDigits();
    } else {
        formatter = new DecimalFormat( format );
        decimalPlaces = formatter.getMaximumFractionDigits();
    }

    // get the result as number
    Double result = null;
    try {
        result = formatter.parse( value ).doubleValue();
    } catch( ParseException ex ) {
        // not bothered at minute
    }

    // round the Double to the precision specified in the format string


    BigDecimal bd = new BigDecimal(result );
    Double roundedValue = bd.setScale( decimalPlaces, RoundingMode.HALF_UP ).doubleValue();

    // output summary
    System.out.println("\tValue = " + value);
    System.out.println( locale == null  ? "\tLocale not specified" : "\tLocale = " + locale.toString());
    System.out.println( format == null || format.length() == 0 ? "\tFormat = Not specified" : "\tFormat = " + format);
    System.out.println("\tResult (Double) = " + result);
    System.out.println("\tRounded Result (Double) (" + decimalPlaces + "dp) = " + roundedValue);
    System.out.println("");
}

This produces the following output:

这将产生以下输出:

Current Locale is nl_BE
Output numbers using the German locale

    Value = 1.000,234567
    Locale = de
    Format = ###,##0.0000
    Result (Double) = 1000.234567
    Rounded Result (Double) (4dp) = 1000.2346

    Value = 1,2345678
    Locale = de
    Format = ###,##0.0000
    Result (Double) = 1.2345678
    Rounded Result (Double) (4dp) = 1.2346

    Value = 1.222.333,234567
    Locale = de
    Format = ###,##0.0000
    Result (Double) = 1222333.234567
    Rounded Result (Double) (4dp) = 1222333.2346

Output numbers using the UK locale

    Value = 1.000,234567
    Locale = en_GB
    Format = ###,##0.0000
    Result (Double) = 1.0
    Rounded Result (Double) (4dp) = 1.0

    Value = 1,2345678
    Locale = en_GB
    Format = ###,##0.0000
    Result (Double) = 1.2345678E7
    Rounded Result (Double) (4dp) = 1.2345678E7

    Value = 1.222.333,234567
    Locale = en_GB
    Format = ###,##0.0000
    Result (Double) = 1.222
    Rounded Result (Double) (4dp) = 1.222



Output numbers using new DecimalFormat( ###,##0.0000 )

    Value = 1.000,234567
    Locale not specified
    Format = ###,##0.0000
    Result (Double) = 1000.234567
    Rounded Result (Double) (4dp) = 1000.2346

    Value = 1,2345678
    Locale not specified
    Format = ###,##0.0000
    Result (Double) = 1.2345678
    Rounded Result (Double) (4dp) = 1.2346

    Value = 1.222.333,234567
    Locale not specified
    Format = ###,##0.0000
    Result (Double) = 1222333.234567
    Rounded Result (Double) (4dp) = 1222333.2346

#3


0  

Sure you can. Try running this:

你当然可以。尝试运行:

String in = "1,234567";
System.out.println(NumberFormat.getNumberFormat(new Locale("fr", "FR")).parse(in));
System.out.println(NumberFormat.getNumberFormat(new Locale("en", "GB")).parse(in));

Clearly they result in different output, the first reading 1.234567 and the second 1234567. Maybe there's something wrong with your pattern? Anyway the last line there would be the preferred way of getting the UK standard format.

显然,它们导致不同的输出,第一个读数为1.234567,第二个读数为1234567。也许你的模式有问题?不管怎样,最后一行是获得英国标准格式的首选方法。

#4


0  

The restriction of decimal places in DecimalFormat is really meant for use in the format() method and doesn't have much effect in the parse() method.

decimal格式中的小数点限制实际上是用于format()方法,在parse()方法中没有太大影响。

In order to get what you want you need this:

为了得到你想要的,你需要:

    try {
        // output current locale we are running under (this happens to be "nl_BE")
        System.out.println("Current Locale is " + Locale.getDefault().toString());

        // number in Central European Format with a format string specified in UK format
        String numberCE = "1,234567"; // 1.234567
        String formatUK = "###,##0.000";

        // do the format
        DecimalFormat formatterUK = new DecimalFormat(formatUK);
        Double valCEWithUKFormat = formatterUK.parse(numberCE).doubleValue();

        // first convert to UK format string
        String numberUK = formatterUK.format(valCEWithUKFormat);
        // now parse that string to a double
        valCEWithUKFormat = formatterUK.parse(numberUK).doubleValue();

        // I want the number to DPs in the format string!!!
        System.out.println("CE Value     " + numberCE + " in UK format (" + formatUK + ") is " + valCEWithUKFormat);

    } catch (ParseException ex) {
        System.out.println("Cannot parse number");
    }

You first need to get the number as a UK format string and then parse that number, using the UK formatter. That will get you the result you're looking for. NB, this will round the number to 3 decimal places, not truncate.

首先需要将数字作为UK格式字符串获取,然后使用UK格式化程序解析该数字。这会得到你想要的结果。NB,这将把数字四舍五入到小数点后三位,而不是截断。

By the way, I'm slightly surprised that your UK formatter is able to parse the CE format number. You really should be parsing the original number with a CE format parser.

顺便说一下,我对您的UK格式化程序能够解析CE格式号感到有点惊讶。您确实应该使用CE格式解析器解析原始数字。