Why can’t we use ‘==’ to compare two float or double numbers [duplicate]

From apidoc, Float.compare:

Compares the two specified float values. The sign of the integer value returned is the same as that of the integer that would be returned by the call:

new Float(f1).compareTo(new Float(f2))

Float.compareTo:

Compares two Float objects numerically. There are two ways in which comparisons performed by this method differ from those performed by the Java language numerical comparison operators (<, <=, ==, >= >) when applied to primitive float values:

  • Float.NaN is considered by this method to be equal to itself and greater than all other float values (including Float.POSITIVE_INFINITY).
  • 0.0f is considered by this method to be greater than -0.0f.

This ensures that the natural ordering of Float objects imposed by this method is consistent with equals.

Consider the following code:

    System.out.println(-0.0f == 0.0f); //true
    System.out.println(Float.compare(-0.0f, 0.0f) == 0 ? true : false); //false      
    System.out.println(Float.NaN == Float.NaN);//false
    System.out.println(Float.compare(Float.NaN, Float.NaN) == 0 ? true : false); //true
    System.out.println(-0.0d == 0.0d); //true
    System.out.println(Double.compare(-0.0d, 0.0d) == 0 ? true : false);//false     
    System.out.println(Double.NaN == Double.NaN);//false
    System.out.println(Double.compare(Double.NaN, Double.NaN) == 0 ? true : false);//true        

The ouput is not correct, since something that is not a number, is simply not a number, and should be treated as equal from number comparison point of view. It is also clear that 0=-0.

Let’s see what Float.compare does:

public static int compare(float f1, float f2) {
   if (f1 < f2)
        return -1;           // Neither val is NaN, thisVal is smaller
    if (f1 > f2)
        return 1;            // Neither val is NaN, thisVal is larger

    int thisBits = Float.floatToIntBits(f1);
    int anotherBits = Float.floatToIntBits(f2);

    return (thisBits == anotherBits ?  0 : // Values are equal
            (thisBits < anotherBits ? -1 : // (-0.0, 0.0) or (!NaN, NaN)
             1));                          // (0.0, -0.0) or (NaN, !NaN)
}

Float.floatToIntBits:

Returns a representation of the specified floating-point value according to the IEEE 754 floating-point “single format” bit layout.
Bit 31 (the bit that is selected by the mask 0x80000000) represents the sign of the floating-point number. Bits 30-23 (the bits that are selected by the mask 0x7f800000) represent the exponent. Bits 22-0 (the bits that are selected by the mask 0x007fffff) represent the significand (sometimes called the mantissa) of the floating-point number.

If the argument is positive infinity, the result is 0x7f800000.

If the argument is negative infinity, the result is 0xff800000.

If the argument is NaN, the result is 0x7fc00000.

In all cases, the result is an integer that, when given to the intBitsToFloat(int) method, will produce a floating-point value the same as the argument to floatToIntBits (except all NaN values are collapsed to a single “canonical” NaN value).

From JLS 15.20.1. Numerical Comparison Operators <, <=, >, and >=

The result of a floating-point comparison, as determined by the specification of the IEEE 754 standard, is:

  • If either operand is NaN, then the result is false.

  • All values other than NaN are ordered, with negative infinity less than all finite values, and positive infinity greater than all finite values.

  • Positive zero and negative zero are considered equal. For example, -0.0<0.0 is false, but -0.0<=0.0 is true.

  • Note, however, that the methods Math.min and Math.max treat negative zero as being strictly smaller than positive zero.

For strict comparisons where operands are positive zero and negative zero the result will be wrong.

From JLS 15.21.1. Numerical Equality Operators == and !=:

The result of a floating-point comparison, as determined by the specification of the IEEE 754 standard, is:

Floating-point equality testing is performed in accordance with the rules of the IEEE 754 standard:

  • If either operand is NaN, then the result of == is false but the result of != is true. Indeed, the test x!=x is true if and only if the value of x is NaN. The methods Float.isNaN and Double.isNaN may also be used to test whether a value is NaN.

  • Positive zero and negative zero are considered equal. For example, -0.0==0.0 is true.

  • Otherwise, two distinct floating-point values are considered unequal by the equality operators. In particular, there is one value representing positive infinity and one value representing negative infinity; each compares equal only to itself, and each compares unequal to all other values.

For equality comparisons where both operands are NaN the result will be wrong.

Since total ordering (=, <, >,<=, >=) is used by many important algorithms (see all the classes that implement the Comparable interface) it is better to use the compare method because it will yield more consistent behavior.

The consequence of the total ordering in the context of the IEEE-754 standard is the difference between the positive and negative zero.

For instance, if you use the equality operator instead of the compare method, and have some collection of values and your code logic makes some decisions based on the ordering of the elements, and you somehow start getting a surplus of NaN values they’ll all be treated as different values instead as the same values.

That may likely produce error in the behavior of the program proportional to the amount/rate of NaN values. And if you have a lot of positive and negative zeroes, that’s just one pair to affect your logic with error.

Float uses IEEE-754 32 bit format and Double uses IEEE-754 64 bit format.

Leave a Comment