简体   繁体   English

Java8中的RoundingMode.HALF_DOWN问题

[英]RoundingMode.HALF_DOWN issue in Java8

I am using jdk 1.8.0_45, and our tests discovered a bug in rouding. 我使用的是jdk 1.8.0_45,我们的测试发现了一个错误。 RoundingMode.HALF_DOWN works the same as RoundingMode.HALF_UP when the last decimal that decide the rounding is 5. 当决定舍入的最后一个小数为5时,RoundingMode.HALF_DOWN与RoundingMode.HALF_UP的工作方式相同。

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. 我发现了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));
    }
}

Actual result: Round down 10.5556 Round up 10.5556 实际结果:回落10.5556回合10.5556

Expected result(obtain with jdk 1.7): Round down 10.5555 Round up 10.5556 预期结果(以jdk 1.7获得):向下舍入10.5555向上舍入10.5556

Seems that it's intended change. 似乎它是有意改变的。 The JDK 1.7 behavior was incorrect. JDK 1.7行为不正确。

The problem is that you simply cannot represent the number 10.55555 using the double type. 问题是你根本无法使用double类型表示数字10.55555 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 . 它以IEEE二进制格式存储数据,因此当您将十进制10.55555数字分配给double变量时,实际上可以得到最接近的值,可以用IEEE格式表示: 10.555550000000000210320649784989655017852783203125 This number is bigger than 10.55555 , so it's correctly rounded to 10.5556 in HALF_DOWN mode. 此数字大于10.55555 ,因此在HALF_DOWN模式下正确舍入到10.5556

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). 例如, 10.1562510 + 5/32 ,因此二进制为1010.00101 )。 This number is rounded to 10.1562 in HALF_DOWN mode and 10.1563 in HALF_UP mode. 这个数字四舍五入为10.1562HALF_DOWN模式和10.1563HALF_UP模式。

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使用BigDecimal.valueOf构造函数,它“翻译一个doubleBigDecimal ,使用double的规范的字符串表示”:

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

The change of behaviour is documented in the release notes of Java 8 行为的更改记录在Java 8的发行说明中

When using the NumberFormat and DecimalFormat classes, the rounding behavior of previous versions of the JDK was wrong in some corner cases. 使用NumberFormatDecimalFormat类时,在某些极端情况下,以前版本的JDK的舍入行为是错误的。 [...] [...]

As an example, when using the default recommended NumberFormatFormat API form: NumberFormat nf = java.text.NumberFormat.getInstance() followed by nf.format(0.8055d) , the value 0.8055d is recorded in the computer as 0.80549999999999999378275106209912337362766265869140625 since this value cannot be represented exactly in the binary format. 例如,当使用默认的建议NumberFormatFormat API形式时: NumberFormat nf = java.text.NumberFormat.getInstance()后跟nf.format(0.8055d) ,值0.8055d在计算机中记录为0.8054999999999999378275106209912337362766265869140625,因为此值不能完全用二进制格式表示。 Here, the default rounding rule is "half-even", and the result of calling format() in JDK 7 is a wrong output of "0.806", while the correct result is "0.805", since the value recorded in memory by the computer is "below" the tie. 这里,默认的舍入规则是“half-even”,而在JDK 7中调用format()的结果是“0.806”的错误输出,而正确的结果是“0.805”,因为记录在内存中的值是电脑是“低于”领带。

This new behavior is also implemented for all rounding positions that might be defined by any pattern chosen by the programmer (non default patterns). 对于可能由程序员选择的任何模式定义的所有舍入位置(非默认模式),也实现了这种新行为。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM