Comparing doubles in Java gives odd results
Asked Answered
G

5

14

I really can'get my head around why the following happens:

Double d = 0.0;
System.out.println(d == 0); // is true
System.out.println(d.equals(0)); // is false ?!

This however works as expected:

Double d = 0.0;
System.out.println(d == 0.0); // true
System.out.println(d.equals(0.0)); // true

I'm positive that this is related to autoboxing in some way, but I really don't know why 0 would be boxed differently when the == operator is used and when .equals is called.

Doesn't this implicitly violate the equals contract ?

  *  It is reflexive: for any non-null reference value
  *     x, x.equals(x) should return
  *     true.

EDIT:

Thanks for the fast answers. I figured that it is boxed differently, the real question is: why is it boxed differently ? I mean that this would be more intuitive if d == 0d than d.equals(0d) is intuitive and expected, however if d == 0 which looks like an Integer is true than 'intuitively' d.equals(0) should also be true.

Gowen answered 8/3, 2011 at 9:34 Comment(0)
K
20

just change it to

System.out.println(d.equals(0d)); // is false ?! now true

You were comparing double with Integer 0

Under the cover

System.out.println(d.equals(0)); // is false ?!

0 will be autoboxed to Integer and an instance of Integer will be passed to equals() method of Double class, where it will compare like

@Override
    public boolean equals(Object object) {
        return (object == this)
                || (object instanceof Double)
                && (doubleToLongBits(this.value) == doubleToLongBits(((Double) object).value));
    }

which is going to return false of course.

Update

when you do comparison using == it compares values so there is no need to autobox , it directly operates on value. Where equals() accepts Object so if you try to invoke d1.equals(0) , 0 is not Object so it will perform autoboxing and it will pack it to Integer which is an Object.

Kienan answered 8/3, 2011 at 9:36 Comment(7)
Or just settle for d == 0? :)Ptarmigan
@Gowen answered your question from commentKienan
How can it compare values when d is an object and 0 is primitive ? IMO it either unboxes d (to int ? or maybe uses Double.doubleValue() ?) or boxes 0, I don't see how it can perform == on an object and a primitive otherwise. Perhaps I should ask a separate question about this since this one is getting a bit tangled ...Gowen
it will invoke d.doubleValue() == 0 , please read more about it in JLS 5.1.7Kienan
So, we could say for d == 0 it is not autoboxed, but autounboxed.Lieu
@Paulo yep :) it doesn't 'touch' the primitive int, rather it calls d.doubleValue() on the Double object. Now I'm wandering if this is JVM implementations specific, but this is another topic :DGowen
@Simeon: this is not JVM specific, since it is specified by the JLS. Jigar Joshi, sorry for the previous comment, I was confused; you're talking about the unboxing conversion, but then you should mention the (docs.oracle.com/javase/specs/jls/se7/html/… 5.1.8].Redfin
B
6

Number objects only equal to numbers with the same value if they are of the same type. That is:

new Double(0).equals(new Integer(0));
new BigInteger("0").equals(new BigDecimal("0"));

and similar combinations are all false.

In your case, the literal 0 is boxed into an Integer object.

Bough answered 8/3, 2011 at 9:36 Comment(3)
Why would it box it to Integer when .equals is called and to something else when == is called ?Gowen
@Gowen because equals() accept Object , check hereKienan
@Jigar I did read your answer (I even upvoted), but what boggles me is that when we do the == comparison 0 is boxed to Double (or is it ?! what is even happening on the == comparison, there should be an object created so it can compare the references correct ?) and when we do .equals its boxed to IntegerGowen
S
5

It's probably worth noting that you should compare floating point numbers like this:

|x - y| < ε, ε very small
Superimpose answered 29/6, 2011 at 10:0 Comment(1)
If right value is not 0.0, such as 0.2323121212,I think your answer is better.Mise
B
2

d.equals(0) : 0 is an int. The Double.equals() code will return true only for Double objects.

Bywaters answered 8/3, 2011 at 9:37 Comment(3)
0 is an int and will be auto-boxed to an Integer.Antherozoid
@Joachim Sauer That is all assumed :)Bywaters
but apparently not understood by the OP. It is a good idea to spell these things out.Anthurium
E
2

When you perform

d == 0

this is upcast to

d == 0.0

however there are no upcasting rules for autoboxing and even if there were equals(Object) gives no hits that you want a Double instead of an Integer.

Exhibitionist answered 8/3, 2011 at 10:7 Comment(1)
So it actually is not boxed at all on == its just cast, this is the answer I was looking for.Gowen

© 2022 - 2024 — McMap. All rights reserved.