[Python-ideas] Why is nan != nan?

Greg Ewing greg.ewing at canterbury.ac.nz
Fri Mar 26 03:01:46 CET 2010

Masklinn wrote:

> Excuse me but NaNs propagate right? So we'd have not only
> float('nan') == float('nan'), but also float('nan') = float('nan') + 1
> right?

The fact that one of the operands was a NaN propagates,
but it doesn't have to be that particular NaN object.
So NaN + 1 could create a new NaN object to return as
its result.

> [Meyer:]
>> It is rather dangerous indeed to depart from the fundamental laws of mathematics. 
> The very idea of IEEE-754 floats are a
> a definite departure from the fundamental laws of mathematics,

They're a departure from the laws of *real numbers*,
but Meyer is using the word "mathematics" in a much
broader sense here -- meaning any formal system
that you can reason about rigorously.

If your formal system has a notion of equality but
includes values that don't compare equal to themselves,
it seriously screws up your ability to reason about it.

For Meyer, this is a *big* problem, because making it
possible to reason rigorously about programs is something
that Eiffel gets really anal about^H^H^H^H^H^H^H^H^H^H
^H^H^H^H^H^H one of the underpinnings of Eiffel's design.

The more I think about it, the more I think that the
only reason for needing NaNs in the first place is if
you don't have, or don't want to use, an exception

As Meyer points out, treating NaN == NaN as false is
not fundamentally any more correct than treating it
as true. One interpretation or the other might be
appropriate in a particular algorithm, but this needs
to be considered on a case-by-case basis.

So the *pythonic* way to handle NaNs is not to have
them at all, but to raise an exception whenever an
operation would produce a NaN. Code that knows what
to do can catch the exception and proceed appropriately.

Another possibility is to extend the boolean type with
a NaB that propagates in the same way as a NaN. But
something still has to give somewhere, because what
happens when you do this?

   x = float('nan')
   y = float('nan')
   if x == y:

If NaN == NaN is a NaB, then neither branch of the
'if' is appopriate, so the only correct thing to do
would be to raise an exception when trying to branch
on a NaB.

So we have three correctness-preserving possibilites:

1) Always raise an exception as soon as a NaN is

2) Propagate NaNs through arithmetic but raise an
    exception when attempting to compare them.

3) Return a NaB when comparing NaNs, and raise an
    exception when attempting to branch on a NaB.


More information about the Python-ideas mailing list