Java8中的RoundingMode.HALF_DOWN问题
我使用的是jdk 1.8.0_45,我们的测试发现了一个错误。 当决定舍入的最后一个小数为5时,RoundingMode.HALF_DOWN与RoundingMode.HALF_UP的工作方式相同。
我发现了RoundingMode.HALF_UP的相关问题,但它们在更新40中得到修复。我还在oracle中添加了一个bug,但根据我的经验,它们确实没有响应。
package testjava8; import java.math.RoundingMode; import java.text.DecimalFormat; public class Formatori { public static void main(String[] args) { DecimalFormat format = new DecimalFormat("#,##0.0000"); format.setRoundingMode(RoundingMode.HALF_DOWN); Double toFormat = 10.55555; System.out.println("Round down"); System.out.println(format.format(toFormat)); format.setRoundingMode(RoundingMode.HALF_UP); toFormat = 10.55555; System.out.println("Round up"); System.out.println(format.format(toFormat)); } }
实际结果:向下舍入10.5556向上舍入10.5556
预期结果(以jdk 1.7获得):向下舍入10.5555向上舍入10.5556
似乎它是有意改变的。 JDK 1.7行为不正确。
问题是你根本无法使用double
类型表示数字10.55555
。 它以IEEE二进制格式存储数据,因此当您将十进制10.55555
数分配给double
变量时,实际上可以得到最接近的值,可以用IEEE格式表示: 10.555550000000000210320649784989655017852783203125
。 此数字大于10.55555
,因此在HALF_DOWN
模式下正确舍入到10.5556
。
您可以检查一些可以用二进制精确表示的数字。 例如, 10.15625
( 10 + 5/32
,因此二进制为1010.00101
)。 此数字在HALF_DOWN
模式下舍入为10.1562
在HALF_DOWN
模式下舍入为10.1562
。
如果要恢复旧行为,可以先使用BigDecimal.valueOf
构造函数将数字转换为BigDecimal
,“使用double
的规范字符串表示forms将double
转换为BigDecimal
”:
BigDecimal toFormat = BigDecimal.valueOf(10.55555); System.out.println("Round down"); System.out.println(format.format(toFormat)); // 10.5555 format.setRoundingMode(RoundingMode.HALF_UP); toFormat = BigDecimal.valueOf(10.55555); System.out.println("Round up"); System.out.println(format.format(toFormat)); // 10.5556
行为的更改记录在Java 8的发行说明中
使用
NumberFormat
和DecimalFormat
类时,在某些极端情况下,以前版本的JDK的舍入行为是错误的。 […]例如,当使用默认的建议
NumberFormatFormat
APIforms:NumberFormat nf = java.text.NumberFormat.getInstance()
后跟nf.format(0.8055d)
,值0.8055d在计算机中记录为0.8054999999999999378275106209912337362766265869140625,因为此值不能完全用二进制格式表示。 这里,默认的舍入规则是“half-even”,而JDK 7中调用format()的结果是“0.806”的错误输出,而正确的结果是“0.805”,因为记录在内存中的值是电脑是“低于”领带。对于可能由程序员选择的任何模式定义的所有舍入位置(非默认模式),也实现了这种新行为。