简体   繁体   English

Java RoundingMode.HALF_EVEN是bug吗?

[英]Is Java RoundingMode.HALF_EVEN bug?

When I rounded a double number with HALF_EVEN mode , I found a problem, I don't know whether it is a bug of JDK? 当我用HALF_EVEN模式舍入一个双号时,我发现了一个问题,我不知道它是否是JDK的一个bug? Please see the following code: 请参阅以下代码:

public static void main(String[] args) {
    RoundingMode mode = RoundingMode.HALF_EVEN;
    for (int i = 10; i < 100; i++) {
        double d = i + 0.55;
        int scale = 1;
        process(d, scale++, mode);

        d = i + 0.555;
        process(d, scale++, mode);

        d = i + 0.5555;
        process(d, scale++, mode);

        d = i + 0.55555;
        process(d, scale++, mode);
        System.out.println("\n");
    }
}

private static void process(double d, int scale, RoundingMode roundingMode) {
    BigDecimal b = new BigDecimal(d).setScale(scale, roundingMode);
    System.out.println(d + " -> " + b);
}

I expect to output: 我希望输出:

10.55 -> 10.6
10.555 -> 10.56
10.5555 -> 10.556
10.55555 -> 10.5556


11.55 -> 11.6
11.555 -> 11.56
11.5555 -> 11.556
11.55555 -> 11.5556

.....

But actually, it outputs data like the below: 但实际上,它输出的数据如下:

10.55 -> 10.6
10.555 -> 10.55
10.5555 -> 10.556
10.55555 -> 10.5556


11.55 -> 11.6
11.555 -> 11.55
11.5555 -> 11.556
11.55555 -> 11.5556
....

30.55 -> 30.6
30.555 -> 30.55
30.5555 -> 30.555
30.55555 -> 30.5556



31.55 -> 31.6
31.555 -> 31.55
31.5555 -> 31.555
31.55555 -> 31.5556



32.55 -> 32.5
32.555 -> 32.55
32.5555 -> 32.556
32.55555 -> 32.5555



33.55 -> 33.5
33.555 -> 33.55
33.5555 -> 33.556
33.55555 -> 33.5555

.........

62.55 -> 62.5
62.555 -> 62.55
62.5555 -> 62.556
62.55555 -> 62.5555



63.55 -> 63.5
63.555 -> 63.55
63.5555 -> 63.556
63.55555 -> 63.5555



64.55 -> 64.5
64.555 -> 64.56
64.5555 -> 64.555
64.55555 -> 64.5555



65.55 -> 65.5
65.555 -> 65.56
65.5555 -> 65.555
65.55555 -> 65.5555



66.55 -> 66.5
66.555 -> 66.56
66.5555 -> 66.555
66.55555 -> 66.5555

There are the same result in JDK 1.7/1.8 JDK 1.7 / 1.8中有相同的结果

Is it a bug of JDK? 这是JDK的错误吗?

It's part of life sadly, and due to the fact that double types are implemented internally with a radix of 2. For scientific programming this is a remarkably clever thing to do. 遗憾的是,它是生活的一部分,并且由于double类型在内部以2的基数实现。对于科学编程,这是一个非常聪明的事情。 But it does mean that when you want to view or specify them with a base 10 radix, what you see is not necessarily what you get. 但它确实意味着当你想要用10基数来查看或指定它们时,你看到的并不一定是你得到的。

For example, the literal 10.555 is actually a number slightly less than this ( 10.5549999999999997157829056959599256515502929687... ) So apparent German rounding will actually round this number downwards . 例如,字面值10.555实际上是一个略小10.555值的数字( 10.5549999999999997157829056959599256515502929687... )因此,明显的德国舍入实际上将向下舍入此数字。 Your question contains many similar cases. 您的问题包含许多类似案例。

This is not a bug as such, but if you need precise rounding then you ought to use a class equipped to represent decimal numbers with arbitrary precision. 这不是一个错误,但是如果你需要精确的舍入,那么你应该使用一个能够以任意精度表示十进制数的类。

Using BigDecimal is an option but you are misusing it horribly! 使用BigDecimal是一个选项,但你可怕地滥用它! . By using the constructor from a double (ie new BigDecimal(d) ) the imprecision endemic in d will be merely copied to the BigDecimal . 通过使用double的构造函数(即new BigDecimal(d) ), d的不精确特性将仅复制到BigDecimal So you ought to use the constructor from a String instead, or some similar approach. 所以你应该使用String的构造函数,或者类似的方法。

Finally, take a look at http://www.exploringbinary.com/floating-point-converter . 最后,请查看http://www.exploringbinary.com/floating-point-converter Type in your decimal number and see the value that it assumes when converted to floating point. 输入您的十进制数,并查看转换为浮点时它所采用的值。

It's not a bug with the rounding mode, it's the way you create the BigDecimal. 这不是舍入模式的错误,它是您创建BigDecimal的方式。 Try the following: 请尝试以下方法:

 BigDecimal b = new BigDecimal(Double.toString(d)).setScale(scale, roundingMode);

To create a BigDecimal you should not create it from the double value as mentioned here . 要创建BigDecimal,您不应该从此处提到的double值创建它。

EDIT: It's better to use valueOf as stated in the comments and in the api 编辑:最好使用评论和api中所述的valueOf

Use BigDecimal b = BigDecimal.valueOf(d).setScale(scale, roundingMode); 使用BigDecimal b = BigDecimal.valueOf(d).setScale(scale, roundingMode); instead. 代替。

As the docs say "This is generally the preferred way to convert a double (or float) into a BigDecimal, as the value returned is equal to that resulting from constructing a BigDecimal from the result of using Double.toString(double)." 正如文档所说:“这通常是将double(或float)转换为BigDecimal的首选方法,因为返回的值等于从使用Double.toString(double)的结果构造BigDecimal所产生的值。”

You should change your process method to: 您应该将您的流程方法更改为:

private static void process(double d, int scale, RoundingMode roundingMode) {
    BigDecimal unscaled = new BigDecimal(d);
    BigDecimal scaled = unscaled.setScale(scale, roundingMode);
    System.out.println(unscaled + " -> " + scaled);
}

That way you see the exact value before scaling. 这样你就可以在缩放之前看到确切的值。

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

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