java - 为什么Math.round( 0.49999999999999994 ) 返回1

  显示原文与译文双语对照的内容

在下面的程序中,你可以看到,除了. 5以外,每个值略微小于。


for (int i = 10; i> = 0; i--) {
 long l = Double.doubleToLongBits(i + 0.5);
 double x;
 do {
 x = Double.longBitsToDouble(l);
 System.out.println(x +" rounded is" + Math.round(x));
 l--;
 } while (Math.round(x)> i);
}

打印


10.5 rounded is 11
10.499999999999998 rounded is 10
9.5 rounded is 10
9.499999999999998 rounded is 9
8.5 rounded is 9
8.499999999999998 rounded is 8
7.5 rounded is 8
7.499999999999999 rounded is 7
6.5 rounded is 7
6.499999999999999 rounded is 6
5.5 rounded is 6
5.499999999999999 rounded is 5
4.5 rounded is 5
4.499999999999999 rounded is 4
3.5 rounded is 4
3.4999999999999996 rounded is 3
2.5 rounded is 3
2.4999999999999996 rounded is 2
1.5 rounded is 2
1.4999999999999998 rounded is 1
0.5 rounded is 1
0.49999999999999994 rounded is 1
0.4999999999999999 rounded is 0

我正在使用 6更新 31.

时间:

摘要

在 Java 6 ( 大概是早期) 中,round(x) 是作为 floor(x+0.5) 实现的。 1 这是一个规范 Bug,确切地说就是这个病理案例。 2 Java 7不再执行这里中断的实现。 3

的问题

0.5+0.49999999999999994在精度上正好是 1:


static void print(double d) {
 System.out.printf("%016xn", Double.doubleToLongBits(d));
}

public static void main(String args[]) {
 double a = 0.5;
 double b = 0.49999999999999994;

 print(a);//3fe0000000000000
 print(b);//3fdfffffffffffff
 print(a+b);//3ff0000000000000
 print(1.0);//3ff0000000000000
}

这是因为 0.499 99999999999994的指数比 0.5小,所以当它们被添加时,它的尾数会被移动,并且ULP会变大。

解决方案

自 Java 7之后,OpenJDK ( 例如) 实现了它: 4


public static long round(double a) {
 if (a!= 0x1.fffffffffffffp-2)//greatest double value less than 0.5
 return (long)floor(a + 0.5d);
 else
 return 0;
}


1.http://docs.oracle.com/javase/6/docs/api/java/lang/Math.html#round%28double%29 web

2.http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6430675 web ( 用于查找这里目的的@SimonNickerson )

3 。http://docs.oracle.com/javase/7/docs/api/java/lang/Math.html#round%28double%29 web

4.http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/7u40-b43/java/lang/Math.java#Math.round%28double%29 web

这似乎是一个已知的Bug ( Java Bug 6430675: Math.round 对 0 x1.fffffffffffffp-2 有惊人的行为,它已经在 Java 7中修复。

JDK 6中的源代码


public static long round(double a) {
 return (long)Math.floor(a + 0.5d);
}

JDK 7中的源代码


public static long round(double a) {
 if (a!= 0x1.fffffffffffffp-2) { 
//a is not the greatest double value less than 0.5
 return (long)Math.floor(a + 0.5d);
 } else {
 return 0; 
 }
}

当该值为 0.499 99999999999994时,在 JDK 6中,它将调用 floor,因此返回 1,但在 JDK 7中,如果该数字的double值小于 0.5或者不小于。 在本例中,数字不是小于 0.5的最大double值,所以块返回 0.

你可以尝试 0.499 99999999999999,这将返回 1,但不是 0,因为这是最大的double值小于 0.5.

我身体里也有一样在 jdk 1.6 32位,但是在 java 7 64位我已经转为 0.499 0 99999999999994圆角是 0和最后一行不会被打印出来。 这似乎是虚拟机问题,但是,使用浮点数,你应该期望结果在不同环境( CPU,32或者 64位 模式) 上有所差异

而且,当使用 round 或者反转矩阵或者 等等 时,这里位可以产生巨大的差异。

x64输出:


10.5 rounded is 11
10.499999999999998 rounded is 10
9.5 rounded is 10
9.499999999999998 rounded is 9
8.5 rounded is 9
8.499999999999998 rounded is 8
7.5 rounded is 8
7.499999999999999 rounded is 7
6.5 rounded is 7
6.499999999999999 rounded is 6
5.5 rounded is 6
5.499999999999999 rounded is 5
4.5 rounded is 5
4.499999999999999 rounded is 4
3.5 rounded is 4
3.4999999999999996 rounded is 3
2.5 rounded is 3
2.4999999999999996 rounded is 2
1.5 rounded is 2
1.4999999999999998 rounded is 1
0.5 rounded is 1
0.49999999999999994 rounded is 0

下面的答案是 Bug 报告的摘录,位于 http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6430675 。 访问链接以获得完整解释。

在方法 {Math, StrictMath.round 正在操作上定义为


(long)Math.floor(a + 0.5d)

于 0相关 x1.fffffffffffffp-2 1的对于双参数。而这一定义通常可以正常工作之后,它给出了惊人的结果,而不是 0,.

值 0.499 99999999999994是最大的floating-point值小于 0.5. 十六进制floating-point文本值为 0 x1.fffffffffffffp-2,等于( 2 - 2 ^52 ) * 2 ^-2. == ( 0.5 - 2 ^54 ) 。 因此,求和的确切值


(0.5 - 2^54) + 0.5

是 1 - 2 ^54. 这是两个相邻的floating-point数字( 1 - 2 ^53 ) 和 1之间的一半。 在硕士论文 754算术舍入到最接近的偶数舍入模式 Java,当一个floating-point结果是不精确的,越接近的两个能表示floating-point值,本文所使用的支架的必须返回;如果两个值都同样接近,那么有一个确切的结果,它的最后一位则返回0. 在本例中,添加的正确返回值是 1,而不是小于 1的最大值。

虽然该方法的工作就像所定义的,该行为在这里输入是非常奇怪;规范coudl是ameneded才能让它更像"四舍五入到最接近的长圆形"这将允许该行为在该输入到被改变的。

共享|改进这里答案

您的答案

丢弃

通过张贴你的答案,你同意隐私策略 。of和服务服务。

不是你要寻找的答案? 浏览其他问题标记 或者 问自己的问题。

...