|
| 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