Java8中的RoundingMode.HALF_DOWN问题

cri*_*sti 8 java migration rounding java-8

我使用的是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));
    }
}
Run Code Online (Sandbox Code Playgroud)

实际结果:向下舍入10.5556向上舍入10.5556

预期结果(以jdk 1.7获得):向下舍入10.5555向上舍入10.5556

Tag*_*eev 10

似乎它是有意改变的.JDK 1.7行为不正确.

问题是你根本无法10.55555double类型表示数字.它以IEEE二进制格式存储数据,因此当您将十进制10.55555数分配给double变量时,实际上可以得到最接近的值,可以用IEEE格式表示:10.555550000000000210320649784989655017852783203125.这个数字比我的大10.55555,所以它的正确四舍五入到10.5556HALF_DOWN模式.

您可以检查一些可以用二进制精确表示的数字.例如,10.15625(10 + 5/32因此1010.00101是二进制).这个数字四舍五入到10.1562HALF_DOWN模式,10.1563HALF_UP模式.

如果要恢复旧行为,可以先将数字转换为BigDecimal使用BigDecimal.valueOf构造函数," 使用规范字符串表示将其double转换为a ":BigDecimaldouble

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
Run Code Online (Sandbox Code Playgroud)


ass*_*ias 5

行为的更改记录在Java 8的发行说明中

使用NumberFormatDecimalFormat类时,在某些极端情况下,以前版本的JDK的舍入行为是错误的.[...]

例如,当使用默认的推荐NumberFormatFormatAPI格式:NumberFormat nf = java.text.NumberFormat.getInstance()后跟时nf.format(0.8055d),值0.8055d在计算机中记录为0.8054999999999999378275106209912337362766265869140625,因为此值无法以二进制格式精确表示.这里,默认的舍入规则是"half-even",而在JDK 7中调用format()的结果是"0.806"的错误输出,而正确的结果是"0.805",因为记录在内存中的值是电脑是"低于"领带.

对于可能由程序员选择的任何模式定义的所有舍入位置(非默认模式),也实现了这种新行为.