Why are floating point infinities, unlike NaNs, equal?

Why doesn't infinity comparison follow the logic applied to NaNs? This code prints out false three times:

double a = Double.NaN;
double b = Double.NaN;
System.out.println(a == b); // false
System.out.println(a < b); //  false
System.out.println(a > b); //  false

However, if I change Double.NaN to Double.POSITIVE_INFINITY, I get true for equality, but false for the greater-than and less-than comparisons:

double a = Double.POSITIVE_INFINITY;
double b = Double.POSITIVE_INFINITY;
System.out.println(a == b); // true
System.out.println(a < b); //  false
System.out.println(a > b); //  false

This seems dangerous. Assuming that infinite values result from overflows, I imagine it's more likely that two variables that ended up as infinities wouldn't actually be equal in perfect arithmetic.


Solution 1:

Your reasoning is that Double.POSITIVE_INFINITY should not be equal to itself because it is “likely” to have been obtained as the result of a loss of accuracy.

This line of reasoning applies to all of floating-point. Any finite value can be obtained as the result of an inaccurate operation. That did not push the IEEE 754 standardization committee to define == as always evaluating to false for finite values, so why should infinities be different?

As defined, == is useful for people who understand what it does (that is, test the floating-point values that have been obtained, and certainly not the values that should have been obtained with real computations). For anyone who understands that, and you need to understand it to use floating-point even for computations that do not involve infinity, having Double.POSITIVE_INFINITY == Double.POSITIVE_INFINITY evaluate to true is convenient, if only to test if the floating-point result of a floating-point computation is Double.POSITIVE_INFINITY.

That leaves the question of why NaN can afford to have special behavior, and infinities should follow the same general principles as finite values. NaN is different from infinities: the underlying principle of the IEEE 754 standard is that values are exactly what they are, but the result of an operation can be approximated with respect to the real result, and in this case, the resulting floating-point value is obtained according to the rounding mode.

Forget for an instant that 1.0 / 0.0 is defined as +inf, which is an annoyance in this discussion. Think for the moment of Double.POSITIVE_INFINITY only as the result of operations such as 1.0e100 / 1.0e-300 or Double.MAX_VALUE + Double.MAX_VALUE. For these operations, +inf is the closest approximation of the real result, just like for operations that produce a finite result. By contrast, NaN is the result you obtain when the operation doesn't make sense. It is defensible to have NaN behave specially, but inf is just an approximation of all the values too large to represent.

In reality, 1.0 / 0.0 also produces +inf, but that should be considered an exception. It would have been just as coherent to define the result of that operation as NaN, but defining it as +inf was more convenient in the implementation of some algorithms. An example is provided page 10 in Kahan's notes. More details than most will wish for are in the article “Branch Cuts for Complex Elementary Functions, or Much Ado About Nothing's Sign Bit”. I would also interpret the existence in IEEE 754 of a “division by zero” flag separate from the NaN flag as recognition that the user may want to treat division by zero specially although it is not defined as producing NaN.

Solution 2:

Because thats the standard. Infinity represents a number greater than or less than Double.MAX_VALUE/-Double.MAX_VALUE.

NaN represents the outcome of an operation that didn't make sense. That is, the operation didn't possibly come out with a number.

I would guess the logic is once a number gets big enough (infinity) and because of the limitation of floating point numbers, adding numbers to it won't change the outcome, so its 'like' infinity.

So if you want to compare to really big numbers, at some point you might just say those two big numbers are close enough for all intents and purposes. But if you want to compare two things that both aren't numbers, you can't compare them so its false. At least you couldn't compare them as a primitive.