Java8 中的 RoundingMode.HALF_DOWN 问题

2022-01-23 00:00:00 rounding migration java-8 java

我使用的是 jdk 1.8.0_45,我们的测试发现了 roding 的一个 bug.RoundingMode.HALF_DOWN 与 RoundingMode.HALF_UP 相同,决定舍入的最后一位小数为 5.

I am using jdk 1.8.0_45, and our tests discovered a bug in rouding. RoundingMode.HALF_DOWN works the same as RoundingMode.HALF_UP when the last decimal that decide the rounding is 5.

我发现了与 RoundingMode.HALF_UP 相关的问题,但它们已在更新 40 中修复.我还向 oracle 提出了一个错误,但根据我的经验,它们确实没有响应.

I found related issues with RoundingMode.HALF_UP, but they are fixed in update 40. I also put a bug to the oracle, but from my experience they are really unresponsive.

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

Actual result: Round down 10.5556 Round up 10.5556

预期结果(使用 jdk 1.7 获得):向下舍入10.5555围捕10.5556

Expected result(obtain with jdk 1.7): Round down 10.5555 Round up 10.5556

推荐答案

看来是打算改了.JDK 1.7 的行为不正确.

Seems that it's intended change. The JDK 1.7 behavior was incorrect.

问题在于您只是不能使用 double 类型表示数字 10.55555.它以 IEEE 二进制格式存储数据,因此当您将十进制 10.55555 数字分配给 double 变量时,您实际上得到了可以用 IEEE 格式表示的最接近的值:10.555550000000000210320649784989655017852783203125.此数字大于 10.55555,因此在 HALF_DOWN 模式下正确四舍五入为 10.5556.

The problem is that you simply cannot represent the number 10.55555 using the double type. It stores the data in IEEE binary format, so when you assign the decimal 10.55555 number to the double variable, you actually get the closest value which can be represented in IEEE format: 10.555550000000000210320649784989655017852783203125. This number is bigger than 10.55555, so it's correctly rounded to 10.5556 in HALF_DOWN mode.

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

You can check some numbers which can be exactly represented in binary. For example, 10.15625 (which is 10 + 5/32, thus 1010.00101 in binary). This number is rounded to 10.1562 in HALF_DOWN mode and 10.1563 in HALF_UP mode.

如果您想恢复旧的行为,您可以先使用 BigDecimal/java/math/BigDecimal.html#valueOf-double-">BigDecimal.valueOf 构造函数,将 double 转换为 BigDecimal,使用 double 的规范字符串表示":

If you want to restore the old behavior, you can first convert your number to BigDecimal using BigDecimal.valueOf constructor, which "translates a double into a BigDecimal, using the double's canonical string representation":

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

相关文章