views:

113

answers:

2

Alternative wording: When will adding Double.MIN_VALUE to a double in Java not result in a different Double value? (See Jon Skeet's comment below)

This SO question about the minimum Double value in Java has some answers which seem to me to be equivalent. Jon Skeet's answer no doubt works but his explanation hasn't convinced me how it is different from Richard's answer.

Jon's answer uses the following:

double d = // your existing value;
long bits = Double.doubleToLongBits(d);
bits++;
d = Double.longBitsToDouble();

Richards answer mentions the JavaDoc for Double.MIN_VALUE

A constant holding the smallest positive nonzero value of type double, 2-1074. It is equal to the hexadecimal floating-point literal 0x0.0000000000001P-1022 and also equal to Double.longBitsToDouble(0x1L).

My question is, how is Double.logBitsToDouble(0x1L) different from Jon's bits++;?

Jon's comment focuses on the basic floating point issue.

There's a difference between adding Double.MIN_VALUE to a double value, and incrementing the bit pattern representing a double. They're entirely different operations, due to the way that floating point numbers are stored. If you try to add a very little number to a very big number, the difference may well be so small that the closest result is the same as the original. Adding 1 to the current bit pattern, however, will always change the corresponding floating point value, by the smallest possible value which is visible at that scale.

I don't see any difference to Jon's approach of incrementing a long, "bits++", with adding Double.MIN_VALUE. When will they produce different results?

I wrote the following code to test the differences. Maybe someone could provide more/better sample double numbers or use a loop to find a number where there is a difference.

    double d = 3.14159269123456789; // sample double
    long bits = Double.doubleToLongBits(d);
    long bitsBefore = bits;
    bits++;
    long bitsAfter = bits;
    long bitsDiff = bitsAfter - bitsBefore;
    long bitsMinValue = Double.doubleToLongBits(Double.MIN_VALUE);
    long bitsSmallValue = Double.doubleToLongBits(Double.longBitsToDouble(0x1L));

    if (bitsMinValue == bitsSmallValue)
    {
        System.out.println("Double.doubleToLongBits(0x1L) is same as Double.doubleToLongBits(Double.MIN_VALUE)");           
    }        

    if (bitsDiff == bitsMinValue)
    {
        System.out.println("bits++ increments the same amount as Double.MIN_VALUE");
    }

    if (bitsDiff == bitsMinValue)
    {
        d = d + Double.MIN_VALUE;
        System.out.println("Using Double.MIN_VALUE");
    }
    else
    {
        d = Double.longBitsToDouble(bits);
        System.out.println("Using doubleToLongBits/bits++");
    }

    System.out.println("bits before: " + bitsBefore);   
    System.out.println("bits after: " + bitsAfter);
    System.out.println("bits diff: " + bitsDiff);   
    System.out.println("bits Min value: " + bitsMinValue);
    System.out.println("bits Small value: " + bitsSmallValue);  

OUTPUT:

Double.doubleToLongBits(Double.longBitsToDouble(0x1L)) is same as Double.doubleToLongBits(Double.MIN_VALUE)
bits++ increments the same amount as Double.MIN_VALUE
Using doubleToLongBits/bits++
bits before: 4614256656636814345
bits after: 4614256656636814346
bits diff: 1
bits Min value: 1
bits Small value: 1
+2  A: 

It's exactly as Jon said:

"If you try to add a very little number to a very big number, the difference may well be so small that the closest result is the same as the original."

For example:

// True:
(Double.MAX_VALUE + Double.MIN_VALUE) == Double.MAX_VALUE
// False:
Double.longBitsToDouble(Double.doubleToLongBits(Double.MAX_VALUE) + 1) == Double.MAX_VALUE)

MIN_VALUE is the smallest representable positive double, but that certainly does not imply that adding it to an arbitrary double results in a unequal one.

In contrast, adding 1 to the underlying bits results in a new bit pattern, and thus does result in a unequal double.

Matthew Flaschen
+3  A: 

Okay, let's imagine it this way, sticking with decimal numbers. Suppose you have a floating decimal point type which allows you to represent 5 decimal digits, and a number between 0 and 3 for the exponent, to multiple the result by 1, 10, 100 or 1000.

So the smallest non-zero value is just 1 (i.e. mantissa=00001, exponent=0). The largest value is 99999000 (mantissa=99999, exponent=3).

Now, what happens when you add 1 to 50000000? You can't represent 50000001...the next representable number after 500000000 is 50001000. So if you try to add them together, the result is just going to be the closest value to the "true" result - which is still 500000000. That's like adding Double.MIN_VALUE to a large double.

My version (converting to bits, incrementing and then converting back) is like taking that 50000000, splitting into mantissa and exponent (m=50000, e=3) then incrementing it the smallest amount, to (m=50001, e=3) and then reassembling to 50001000.

Do you see how they're different?


Now here's a concrete example:

public class Test{
    public static void main(String[] args) {
        double before = 100000000000000d;
        double after = before + Double.MIN_VALUE;
        System.out.println(before == after);

        long bits = Double.doubleToLongBits(before);
        bits++;
        double afterBits = Double.longBitsToDouble(bits);
        System.out.println(before == afterBits);
        System.out.println(afterBits - before);
    }
}

This tries both approaches with a large number. The output is:

true
false
0.015625

Going through the output, that means:

  • Adding Double.MIN_VALUE didn't have any effect
  • Incrementing the bit did have an effect
  • The difference between afterBits and before is 0.015625, which is much bigger than Double.MIN_VALUE. No wonder the simple addition had no effect!
Jon Skeet