即使明确设置,DecimalFormat也使用不正确的RoundingMode

时间:2016-05-13 04:15:23

标签: java rounding decimalformat

为什么这个DecimalFormat没有按预期使用RoundingMode.HALF_UP进行舍入,以及应该采取什么措施来获得预期的输出?是否可以使用DecimalFormat完成?

DecimalFormat df = new DecimalFormat("0.0");

df.setRoundingMode(RoundingMode.HALF_UP);

df.format(0.05); // expecting 0.1, getting 0.1
df.format(0.15); // expecting 0.2, getting 0.1  << unexpected

df.format(0.06); // expecting 0.1, getting 0.0  << unexpected

我已经看到了来自this question的答案,特别是this answer,但它似乎只有在舍入到整数时才有效。

我的JDK是8.0.110而我的JRE是8.0.730.2

3 个答案:

答案 0 :(得分:4)

(以下使用Java 8回答。)

您看到的问题来自于指定&#34; 0.15&#34;或&#34; 0.05&#34;在代码中,当表示为double时,略小于0.15。看看这个

DecimalFormat df = new DecimalFormat("#.#");

df.setRoundingMode(RoundingMode.HALF_UP);

BigDecimal bd = new BigDecimal(0.15);
System.out.println("bd=" + bd);
System.out.println(df.format(0.15)); // expecting 0.1, getting 0.1
bd = new BigDecimal(0.05);
System.out.println("bd=" + bd);
System.out.println(df.format(0.05));
bd = new BigDecimal(0.06);
System.out.println("bd=" + bd);
System.out.println(df.format(0.06));

此代码的输出是

bd=0.1499999999999999944488848768742172978818416595458984375
0.1
bd=0.05000000000000000277555756156289135105907917022705078125
0.1
bd=0.059999999999999997779553950749686919152736663818359375
0.1

一个可能的解决方案(如果你绝对需要它以正确的方式进行舍入)是使用BigDecimal.valueOf来创建值。例如

BigDecimal bd = BigDecimal.valueOf(0.15);
System.out.println("bd=" + bd);
System.out.println(df.format(bd)); // expecting 0.1, getting 0.1
bd = BigDecimal.valueOf(0.05);
System.out.println("bd=" + bd);
System.out.println(df.format(bd));
bd = BigDecimal.valueOf(0.06);
System.out.println("bd=" + bd);
System.out.println(df.format(bd));

现在会产生

bd=0.15
0.2
bd=0.05
0.1
bd=0.06
0.1
顺便说一下,正如Scary Wombat指出的那样,掩码设置为0.0而不是#。#将使0.6为0.但我认为这是后来的编辑,然后我开始查看它。使用#。#。

答案 1 :(得分:4)

当你这样做时

df.format(0.15);

实际上有两个舍入操作。显而易见的是df.format所要求的那个,但是在编译时会发生更早的那个。

十进制值0.15不能表示为double。双打只能表示分母为2的幂的有理数,就像十进制表示法只能代表其分母为10的幂的有理数。当你编写0.15时,编译器会将其舍入到可以表示为double的最接近的值,并且该值恰好是

0.1499999999999999944488848768742172978818416595458984375

df.format正确向下舍入。它不完全在0.1和0.2之间,所以HALF_UP舍入模式并不重要。

至于

df.format(0.06); // expecting 0.1, getting 0.0  << unexpected

如果您正在查看 ,那就是一个错误。看起来它匹配one linked by ScaryWombat,据报道固定在8u40和8u45。使用8u51的Ideone测试显示正确的行为。更新Java应该可以解决问题。

答案 2 :(得分:0)

我认为答案是IEEE Standard for Floating-Point Arithmetic (IEEE 754). Wiki文章在Rounding rules部分有一个很好的例子。您还可以阅读更多关于浮点的9.1 of Introduction to Java部分。 BigDecimal通过将未调用的值存储在BigInteger中来解决大部分这些短缺,精度和比例在整数字段中分开。