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”

+1

that _is_ mysterious! – tofutim

+0

也许你将不得不乘以10并再次除以变通方法 – tofutim

+1

在1.7工作正常 也许看到http://stackoverflow.com/questions/22797964/is-inconsistency-in-rounding-between-java-7 -and-java-8-a-bug –

,当它表示为双精度时略小于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。但我认为这是后来编辑然后当我开始看着它。使用 #。#。

+1

为什么downvote? :( – tofutim

+0

所以问题是'DecimalFormat df = new DecimalFormat(“0.0”);'vs'DecimalFormat df = new DecimalFormat(“#。#”);'? –

+0

@ScaryWombat他没有这么说。考虑到? – EJP

当你

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应该可以解决这个问题。

+3

我喜欢这个:“双打只能表示分母是两个幂的分母” – tofutim

+1

然后你可以解释'df.format(0.06); //期望0.1,得到0.0“意外” - 也就是为什么确定为1.7而不是1.8? –

+1

@ 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中存储未调用的值解决了大部分这些短小问题,精度和范围分别存储在整数字段中。