DecimalFormat即使在明确设置时也使用不正确的RoundingMode
为什么此DecimalFormat
未按预期使用RoundingMode.HALF_UP
进行舍入,以及应该如何获得预期输出?可以用DecimalFormat
完成吗?DecimalFormat即使在明确设置时也使用不正确的RoundingMode
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
(以下使用Java 8回答)你所看到的问题来自于指定代码 “0.15” 或 “0.05”
,当它表示为双精度时略小于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
顺便说一句,因为可怕的袋熊指出,掩码设定为0.0,而不是#,#将0.6 0。但我认为这是后来编辑然后当我开始看着它。使用 #。#。
当你
df.format(0.15);
实际上有取整运算。显而易见的是你用df.format
要求的那个,但是在编译时会有一个更早的。
十进制值0.15不能表示为双精度型。双打只能表示分母是二的幂的分母,就像十进制符号只能表示分母是十的幂的分母。当你写0.15
,编译器几轮这最接近的值表示为一个双,和值恰好是
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应该可以解决这个问题。
我喜欢这个:“双打只能表示分母是两个幂的分母” – tofutim
然后你可以解释'df.format(0.06); //期望0.1,得到0.0“意外” - 也就是为什么确定为1.7而不是1.8? –
@ user2357112在第一次提到0.05的例子之后,您是否打算使用0.0 ...而不是0.1 ...?还有,精湛的答案。 –
我认为答案是IEEE Standard for Floating-Point Arithmetic (IEEE 754). Wiki文章在Rounding rules部分有一个很好的例子。你也可以阅读9.1 of Introduction to Java的章节,了解更多关于浮点的内容。 BigDecimal通过在BigInteger中存储未调用的值解决了大部分这些短小问题,精度和范围分别存储在整数字段中。
that _is_ mysterious! – tofutim
也许你将不得不乘以10并再次除以变通方法 – tofutim
在1.7工作正常 也许看到http://stackoverflow.com/questions/22797964/is-inconsistency-in-rounding-between-java-7 -and-java-8-a-bug –