[英]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.