[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
mechanism.

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:
     ...
   else:
     ...

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
    produced.

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.

-- 
Greg



More information about the Python-ideas mailing list