+0 and -0 shows different behavior for int and float data
For the integers, there is no distinction between -0 and 0 for integers because it uses Twos compliment representation. So your integer example i
and i1
are exactly the same.
For the floats, there is a -0 representation, and it's value is equivalent to 0, but the bit representation is different. Hence new Float(0f) and new Float(-0f) would have different representations.
You can see the difference in the bit representations.
System.out.println(Float.floatToIntBits(-0f) + ", " + Float.floatToIntBits(0f));
-2147483648, 0
And if you leave off the f
to declare the -0f
then it will be treated as an integer, and you won't see any difference in the output.
Ints and floats are pretty different beasts in Java. Ints are encoded as two's complement, which has a single 0 value. Floats use IEEE 754 (the 32-bit variant for floats, and 64-bit for doubles). IEEE 754 is somewhat complex, but for purpose of this answer, you just need to know that it has three sections, the first of which is a sign bit. That means for any float, there's a positive and negative variant¹. That includes 0, so floats actually have two "zero" values, +0 and -0.
As an aside, the two's complement that ints use is not the only way to encode integers in computer science. There are other methods, like ones' complement, but they have quirks — like having both a +0 and -0 as distinct values. ;-)
When you compare float primitives (and doubles), Java treats +0 and -0 as equal. But when you box them, Java treats them separately, as described in Float#equals
. This lets the equals method be consistent with their hashCode
implementation (as well as compareTo
), which just uses the bits of the float (including that signed value) and shoves them as-is into an int.
They could have picked some other option for equals/hashCode/compareTo, but they didn't. I'm not sure what the design considerations there were. But in at least one regard, Float#equals
was always going to diverge from the float primitive's ==
: In primitives, NaN != NaN
, but for all objects, o.equals(o)
must also be true. That means that if you had Float f = Float.NaN
, then f.equals(f)
even though f.floatValue() != f.floatValue()
.
¹ NaN (not-a-number) values have a sign bit, but it doesn't have any meaning other than for ordering, and Java ignores it (even for ordering).
This is one of Float equals exception
there are two exceptions:
If f1 represents +0.0f while f2 represents -0.0f, or vice versa, the equal test has the value false
The why is described also:
This definition allows hash tables to operate properly.
-0 and 0 will represented differently using Float's bit 31:
Bit 31 (the bit that is selected by the mask 0x80000000) represents the sign of the floating-point number.
This isn't the case in Integer