简体   繁体   中英

Why does Double.NaN equal itself when wrapped in a Double instance?

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?

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.

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.

If one has two immutable objects which define Equals to return true when every field in one object reports itself Equal to the corresponding field in the other, and if none of the code which uses the objects cares about reference equality, then it should be possible to replace all references to one object with references to the other. If the objects are generated by eg parsing data read from disk, and if many objects will compare equal, replacing many different instances with references to one instance may improve performance greatly. Note that the correctness of such substitution does not depend upon whether the objects are shallowly or deeply immutable, provided only that any mutable objects nested within them will report themselves as unequal to anything other than themselves.

For such logic to work correctly, it's important that the objects in question be fully equivalent. Every object is required to be equal to itself, so Double.NaN must equal Double.NaN . The requirement for full equivalence implies that positive zero must not report itself equal to negative zero (since if it did, an object which holds a positive zero might behave differently from one holding negative zero).

Note, btw, that this is an area where .net differs from Java (some non-equivalent floating-point and Decimal values are report Equals ); in many ways, I think .net is superior, but this detail is one that .net got wrong.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM