In the following program you can see that each value slightly less than .5
is rounded down, except for 0.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); }
prints
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
I am using Java 6 update 31.
5 to be rounded is rounded either up or down so that the result of the rounding is always an even number. Thus 2.5 rounds to 2.0, 3.5 to 4.0, 4.5 to 4.0, 5.5 to 6.0, and so on.
round() is a built-in math function which returns the closest long to the argument. The result is rounded to an integer by adding 1/2, taking the floor of the result after adding 1/2, and casting the result to type long. If the argument is NaN, the result is 0.
Round(decimal) will always return an integral value, these overloads still cannot return an integer value type.
Summary
In Java 6 (and presumably earlier), round(x)
is implemented as floor(x+0.5)
.1 This is a specification bug, for precisely this one pathological case.2 Java 7 no longer mandates this broken implementation.3
The problem
0.5+0.49999999999999994 is exactly 1 in double precision:
static void print(double d) { System.out.printf("%016x\n", 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 }
This is because 0.49999999999999994 has a smaller exponent than 0.5, so when they're added, its mantissa is shifted, and the ULP gets bigger.
The solution
Since Java 7, OpenJDK (for example) implements it thus: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
2. http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6430675 (credits to @SimonNickerson for finding this)
3. http://docs.oracle.com/javase/7/docs/api/java/lang/Math.html#round%28double%29
4. http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/7u40-b43/java/lang/Math.java#Math.round%28double%29
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With