tags:

views:

314

answers:

2

From this question I learned Double.NaN is not equal to itself.

I was verifying this for myself and noticed this is not the case if you wrap Double.NaN in a Double instance. For example:

public class DoubleNaNTest {
    public static void main(String[] args) {
        double primitive = Double.NaN;
        Double object = new Double(primitive);

        // test 1 - is the primitive is equal to itself?
        boolean test1 = primitive == primitive;

        // test 2 - is the object equal to itself?
        boolean test2 = object.equals(object);

        // test 3 - is the double value of the object equal to itself?
        boolean test3 = object.doubleValue() == object.doubleValue();

        System.out.println("Test 1 = " + test1);
        System.out.println("Test 2 = " + test2);
        System.out.println("Test 3 = " + test3);
    }
}

Outputs:

Test 1 = false
Test 2 = true
Test 3 = false

It seems to me that all three tests should evaluate to false as all three operations are equivalent (as they are if you use something other then Double.NaN).

Could someone explain what's going on here?

+5  A: 

It's so hash tables work right

They deliberately deviated from the IEEE way so hash tables would work.

You can get part of the story in the api docs. The rest of the story is: every class that inherits from Object, which is all of them, has a contract to maintain the Object invariants. The contract exists so that the rest of the library can implement all those nice collections and things. Nothing really stops you from damaging this, but then you can't be certain that things that take Object's will work.

DigitalRoss
+1 - I like the Paul Harvey part (and now for the rest of the story)
James Black
+6  A: 

What is going on is that the equals method deliberately deviates from IEE floating point. Quoting from the Javadoc for the equals(Object) method of java.lang.Double.

However, there are two exceptions:

  • If d1 and d2 both represent Double.NaN, then the equals method returns true, even though Double.NaN==Double.NaN has the value false.
  • If d1 represents +0.0 while d2 represents -0.0, or vice versa, the
    equal test has the value false, even
    though +0.0==-0.0 has the value true.

This definition allows hash tables to operate properly.

The upshot is that if you want 100% IEE floating point compatibility you need to explicitly unbox the java.lang.Double instances and compare the resulting double values.

Stephen C
Thanks - I was only experimenting, but if I ever do anything where this could matter I'm glad I know about this quirk.
Chris Harcourt