1 view
in Java

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.

by (46k points)

In Java 6 and previous, round(x) is executed as floor(x+0.5). This is a spec bug, for specifically this one neurotic case. Java 7 and above no longer mandates this broken implementation.

The problem

0.5+0.49999999999999994 is exactly 1 in dual accuracy:

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 happening because 0.49999999999999994 has a smaller index than 0.5, so when they're added, its mantissa is stirred, and the ULP gets bigger.

Solution:

After Java 7, OpenJDK (for example) executes it thus:

public static long round(double a) {

if (a != 0x1.fffffffffffffp-2) // biggest double value less than 0.5

return (long)floor(a + 0.5d);

else

return 0;

}