Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Math.round(0.49999999999999994) return 1?

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.

like image 729
Peter Lawrey Avatar asked Mar 28 '12 07:03

Peter Lawrey


People also ask

Why does math round round down?

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.

Does math round return a long?

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.

Does round return an int?

Round(decimal) will always return an integral value, these overloads still cannot return an integer value type.


1 Answers

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

like image 187
Oliver Charlesworth Avatar answered Oct 14 '22 22:10

Oliver Charlesworth