Search code examples
javafloating-pointdoublefloor

Rounding double strangeness


it might be too late in the night, but I can't understand the behavior of this code:

public class DT {
static void theTest(double d){
    double e = Math.floor(d/1630)*1630;
    System.out.println(e-d);
}

public static void main(String[] args) {
    theTest(2*1630);
    theTest(2*1631);
    theTest(2*1629);
    theTest(8.989779443802325E18);
}

}

in my understangind, all 4 cases should be NON-positive, i.e. "e" is always <= "d", but I do get following output:

0.0
-2.0
-1628.0
1024.0

Why??. as this is same with FastMath, I suspect something double-specific? but could anyone explain me this?


Solution

  • When you get up into huge numbers, the doubles are spaced more widely than integers. When you do a division in this range, the result can be rounded up or down. So in your fourth test case, the result of the division d/1630 is actually rounded up to the nearest available double. Since this is a whole number, the call to floor does not change it. Multiplying it by 1630 then gives a result that is larger than d.

    Edit

    This effect kicks in at 2^52. Once you get past 2^52, there are no more non-integer doubles. Between 2^52 and 2^53, the doubles are just the integers. Above 2^53, the doubles are spaced more widely than the integers.

    The result of the division in the question is 5515202112762162.576... which is between 2^52 and 2^53. It is rounded to the nearest double, which is the same as the nearest integer, which is 5515202112762163. Now, the floor does not change this number, and the multiplication by 1630 gives a result that is larger than d.

    In summary, I guess the first sentence of the answer was a little misleading - you don't need the doubles to be spaced more widely than the integers for this effect to occur; you only need them to be spaced at least as widely as the integers.

    With a value of d between 0 and 2^52 * 1630, the program in the question will never output a positive number.