Java - Decimal Format.parse 返回具有指定小数位数的双精度值

Posted

技术标签:

【中文标题】Java - Decimal Format.parse 返回具有指定小数位数的双精度值【英文标题】:Java - Decimal Format.parse to return double value with specified number of decimal places 【发布时间】:2011-06-11 22:49:18 【问题描述】:

我希望能够将字符串转换为 Double 给定格式字符串中的小数位数。所以“###,##0.000”应该给我一个到小数点后三位。

编辑 - 为发生的事情添加更多信息

用户在 UI 中输入值 - 输入到字符串中。规则是此值限制为小数点后 3 位。底层代码将值存储在数据库中,然后用于计算。因此,尾随小数位将导致计算结果与预期略有偏差。

我有以下代码:

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");
    

DecimalFormat 似乎忽略了格式字符串,并给了我完整的字符串作为 1.234567 的 Double

解析时是否可以强制使用DecimalFormat格式字符串?我错过了什么吗?

干杯,

安德斯

【问题讨论】:

你真的想尝试这样做吗?由于在从 base-10 转换为二进制(内部表示)时会丢失精度,因此您可能无法得到预期的结果。 【参考方案1】:

DecimalFormat 用于两个不同目的:解析输入和格式化输出。如果你想两者都做,你必须使用格式对象两次。

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

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

这会给你1,235的输出

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

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

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);

一些注意事项:

输入解析应与输出格式分开。尤其是当您开始输入多个语言环境时。 允许标准库执行繁重的工作以确定给定区域设置的有效输入值是什么。你只需要选择一个合适的语言环境(我选择了德国,但这显然可以与其他人一起使用)。尽可能使用语言环境。不要尝试重新创建格式化字符串。 始终将输入值与任何输出格式分开存储。 IE,如果您只想在输出中显示三位数字,那很好,但无论如何都要存储整个双精度值。

【讨论】:

【参考方案2】:

考虑到你所说的,我稍微修改了我的代码以涵盖不同的语言环境。关键是将本地化格式的值字符串转换为基于格式字符串四舍五入的 Double。

格式字符串始终是基于英国的格式,小数分隔符指定为“。”千位分隔符指定为“,”。

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

初始代码结构已被修改,以便您查看在不同语言环境下会发生什么感谢@RD01 注意到其他语言环境的重要性。

我现在有如下代码:

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("");

这会产生以下输出:

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】:

DecimalFormat 中的小数位限制实际上是在format() 方法中使用的,在parse() 方法中没有太大影响。

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

    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");
    

您首先需要将数字作为英国格式字符串获取,然后使用英国格式化程序解析该数字。这将为您提供您正在寻找的结果。注意,这会将数字四舍五入到小数点后 3 位,而不是截断。

顺便说一句,我有点惊讶您的英国格式化程序能够解析 CE 格式编号。您确实应该使用 CE 格式解析器来解析原始数字。

【讨论】:

【参考方案4】:

当然可以。尝试运行:

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));

显然它们会产生不同的输出,第一个读数 1.234567 和第二个读数 1234567。也许你的模式有问题?无论如何,最后一行将是获取UK 标准格式的首选方式。

【讨论】:

以上是关于Java - Decimal Format.parse 返回具有指定小数位数的双精度值的主要内容,如果未能解决你的问题,请参考以下文章

decimal相当于java啥类型

decimal 在JAVA里怎么表示?

Java 为啥不提供decimal

mysql字段类型为decimal,则java 实体类的对应属性类型应该为啥

java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal res

java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal res