Skip to content

Commit b0adca9

Browse files
authored
Merge pull request giantray#90 from RWBUPT/patch-2
Create why-does-math.round-(0.49999999999999994)-return-1.md
2 parents 2630b3e + 9ceec4f commit b0adca9

File tree

1 file changed

+92
-0
lines changed

1 file changed

+92
-0
lines changed
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
##为什么Math.round(0.49999999999999994)返回1?
2+
3+
###问题描述:
4+
5+
在下面的程序中你可以看到每个稍微比.5小的值都被向下舍入了,除了`0.5`那个。
6+
7+
```
8+
for (int i = 10; i >= 0; i--) {
9+
long l = Double.doubleToLongBits(i + 0.5);
10+
double x;
11+
do {
12+
x = Double.longBitsToDouble(l);
13+
System.out.println(x + " rounded is " + Math.round(x));
14+
l--;
15+
} while (Math.round(x) > i);
16+
}
17+
```
18+
输出
19+
```
20+
10.5 rounded is 11
21+
10.499999999999998 rounded is 10
22+
9.5 rounded is 10
23+
9.499999999999998 rounded is 9
24+
8.5 rounded is 9
25+
8.499999999999998 rounded is 8
26+
7.5 rounded is 8
27+
7.499999999999999 rounded is 7
28+
6.5 rounded is 7
29+
6.499999999999999 rounded is 6
30+
5.5 rounded is 6
31+
5.499999999999999 rounded is 5
32+
4.5 rounded is 5
33+
4.499999999999999 rounded is 4
34+
3.5 rounded is 4
35+
3.4999999999999996 rounded is 3
36+
2.5 rounded is 3
37+
2.4999999999999996 rounded is 2
38+
1.5 rounded is 2
39+
1.4999999999999998 rounded is 1
40+
0.5 rounded is 1
41+
0.49999999999999994 rounded is 1
42+
0.4999999999999999 rounded is 0
43+
```
44+
我使用 Java 6 update 31。
45+
46+
###回答:
47+
48+
**总结**
49+
50+
在Java 6中(而且很可能更早),`round x``floor(x+0.5)`的方式执行[1]。这是一个规则上的bug,它恰好会导致这样一种不合道理的情况[2]。Java 7不再授权这种分离的执行方式[3]
51+
52+
**问题**
53+
54+
0.5+0.49999999999999994在双精度数中正好表示1:
55+
```
56+
static void print(double d) {
57+
System.out.printf("%016x\n", Double.doubleToLongBits(d));
58+
}
59+
60+
public static void main(String args[]) {
61+
double a = 0.5;
62+
double b = 0.49999999999999994;
63+
64+
print(a); // 3fe0000000000000
65+
print(b); // 3fdfffffffffffff
66+
print(a+b); // 3ff0000000000000
67+
print(1.0); // 3ff0000000000000
68+
}
69+
```
70+
这是因为0.49999999999999994有一个比0.5小的指数,所以它们相加时,它的尾数被移位了,ULP(ULP表示现有数值最后一位代表的最小数值单位,小于ULP的数值将无法累加到现有数值)变的更大。
71+
72+
**解决方法**
73+
74+
从Java 7开始,OpenJDK(如下例)这样执行它(round函数)[4]
75+
```
76+
public static long round(double a) {
77+
if (a != 0x1.fffffffffffffp-2) // greatest double value less than 0.5
78+
return (long)floor(a + 0.5d);
79+
else
80+
return 0;
81+
}
82+
```
83+
[1] http://docs.oracle.com/javase/6/docs/api/java/lang/Math.html#round%28double%29
84+
85+
[2] http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6430675 (credits to @SimonNickerson for finding this)
86+
87+
[3] http://docs.oracle.com/javase/7/docs/api/java/lang/Math.html#round%28double%29
88+
89+
[4] http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/7u40-b43/java/lang/Math.java#Math.round%28double%29
90+
91+
92+
[stackoverflow链接:Why does Math.round(0.49999999999999994) return 1?](http://stackoverflow.com/questions/9902968/why-does-math-round0-49999999999999994-return-1)

0 commit comments

Comments
 (0)