Why do comparisons of NaN values behave differently from all other values? That is, all comparisons with the operators ==, <=, >=, <, > where one or both values is NaN returns false, contrary to the behaviour of all other values.
I suppose this simplifies numerical computations in some way, but I couldn't find an explicitly stated reason, not even in the Lecture Notes on the Status of IEEE 754 by Kahan which discusses other design decisions in detail.
This deviant behavior is causing trouble when doing simple data processing. For example, when sorting a list of records w.r.t. some real-valued field in a C program I need to write extra code to handle NaN as the maximal element, otherwise the sort algorithm could become confused.
Edit: The answers so far all argue that it is meaningless to compare NaNs.
I agree, but that doesn't mean that the correct answer is false, rather it would be a Not-a-Boolean (NaB), which fortunately doesn't exist.
So the choice of returning true or false for comparisons is in my view arbitrary, and for general data processing it would be advantageous if it obeyed the usual laws (reflexivity of ==, trichotomy of <, ==, >), lest data structures which rely on these laws become confused.
So I'm asking for some concrete advantage of breaking these laws, not just philosophical reasoning.
Edit 2: I think I understand now why making NaN maximal would be a bad idea, it would mess up the computation of upper limits.
NaN != NaN might be desirable to avoid detecting convergence in a loop such as
while (x != oldX) {
oldX = x;
x = better_approximation(x);
}
which however should better be written by comparing the absolute difference with a small limit. So IMHO this is a relatively weak argument for breaking reflexivity at NaN.
To throw in yet another analogy. If I hand you two boxes, and tell you that neither of them contains an apple, would you tell me that the boxes contain the same thing?
NaN contains no information about what something is, just what it isn't. Therefore these elements can never definitely be said to be equal.
I'm guessing that NaN (Not A Number) means exactly that: This is not a number and thus comparing it does not really make sense.
It's a bit like arithmetic in SQL with
null
operands: They all result innull
.The comparisons for floating point numbers compare numeric values. Thus, they can't be used for non numeric values. NaN therefore cannot be compared in a numeric sense.
While I agree that comparisons of NaN with any real number should be unordered, I think there is just cause for comparing NaN with itself. How, for example does one discover the difference between signaling NaNs and quiet NaNs? If we think of the signals as a set of Boolean values (i.e. a bit-vector) one might well ask whether the bit-vectors are the same or different and order the sets accordingly. For example, on decoding a maximum biased exponent, if the significand were left shifted so as to align the most significant bit of the significand on the most significant bit of the binary format, a negative value would be a quiet NaN and any positive value would be a signaling NaN. Zero of course is reserved for infinity and the comparison would be unordered. MSB alignment would allow for the direct comparison of signals even from different binary formats. Two NaNs with the same set of signals would therefore be equivalent and give meaning to equality.
It only looks peculiar because most programming environments that allow NaNs do not also allow 3-valued logic. If you throw 3-valued logic into the mix, it becomes consistent:
Even .NET does not provide a
bool? operator==(double v1, double v2)
operator, so you are still stuck with the silly(NaN == NaN) = false
result.I was a member of the IEEE-754 committee, I'll try to help clarify things a bit.
First off, floating-point numbers are not real numbers, and floating-point arithmetic does not satisfy the axioms of real arithmetic. Trichotomy is not the only property of real arithmetic that does not hold for floats, nor even the most important. For example:
I could go on. It is not possible to specify a fixed-size arithmetic type that satisfies all of the properties of real arithmetic that we know and love. The 754 committee has to decide to bend or break some of them. This is guided by some pretty simple principles:
Regarding your comment "that doesn't mean that the correct answer is false", this is wrong. The predicate
(y < x)
asks whethery
is less thanx
. Ify
is NaN, then it is not less than any floating-point valuex
, so the answer is necessarily false.I mentioned that trichotomy does not hold for floating-point values. However, there is a similar property that does hold. Clause 5.11, paragraph 2 of the 754-2008 standard:
As far as writing extra code to handle NaNs goes, it is usually possible (though not always easy) to structure your code in such a way that NaNs fall through properly, but this is not always the case. When it isn't, some extra code may be necessary, but that's a small price to pay for the convenience that algebraic closure brought to floating-point arithmetic.
Addendum: Many commenters have argued that it would be more useful to preserve reflexivity of equality and trichotomy on the grounds that adopting NaN != NaN doesn’t seem to preserve any familiar axiom. I confess to having some sympathy for this viewpoint, so I thought I would revisit this answer and provide a bit more context.
My understanding from talking to Kahan is that NaN != NaN originated out of two pragmatic considerations:
That
x == y
should be equivalent tox - y == 0
whenever possible (beyond being a theorem of real arithmetic, this makes hardware implementation of comparison more space-efficient, which was of utmost importance at the time the standard was developed — note, however, that this is violated for x = y = infinity, so it’s not a great reason on its own; it could have reasonably been bent to(x - y == 0) or (x and y are both NaN)
).More importantly, there was no
isnan( )
predicate at the time that NaN was formalized in the 8087 arithmetic; it was necessary to provide programmers with a convenient and efficient means of detecting NaN values that didn’t depend on programming languages providing something likeisnan( )
which could take many years. I’ll quote Kahan’s own writing on the subject:Note that this is also the logic that rules out returning something like a “Not-A-Boolean”. Maybe this pragmatism was misplaced, and the standard should have required
isnan( )
, but that would have made NaN nearly impossible to use efficiently and conveniently for several years while the world waited for programming language adoption. I’m not convinced that would have been a reasonable tradeoff.To be blunt: the result of NaN == NaN isn’t going to change now. Better to learn to live with it than to complain on the internet. If you want to argue that an order relation suitable for containers should also exist, I would recommend advocating that your favorite programming language implement the
totalOrder
predicate standardized in IEEE-754 (2008). The fact that it hasn’t already speaks to the validity of Kahan’s concern that motivated the current state of affairs.The over-simplified answer is that a NaN has no numeric value, so there is nothing in it to compare to anything else.
You might consider testing for and replacing your NaNs with +INF if you want them to act like +INF.