float("nan") in set or as key

John Nagle nagle at animats.com
Sun May 29 02:12:54 EDT 2011


On 5/28/2011 6:04 PM, Gregory Ewing wrote:
> MRAB wrote:
>> float("nan") can occur multiple times in a set or as a key in a dict:
>>
>> >>> {float("nan"), float("nan")}
>> {nan, nan}
>>
>> except that sometimes it can't:
>>
>> >>> nan = float("nan")
>> >>> {nan, nan}
>> {nan}
>
> NaNs are weird. They're not equal to themselves:
>
> Python 2.7 (r27:82500, Oct 15 2010, 21:14:33)
> [GCC 4.2.1 (Apple Inc. build 5664)] on darwin
> Type "help", "copyright", "credits" or "license" for more information.
>  >>> nan = float("nan")
>  >>> nan == nan
> False
>
> This confuses the daylights out of Python's dict lookup machinery,
> which assumes that two references to the same object can't possibly
> compare unequal, so it doesn't bother calling __eq__ on them.

    Right.

    The correct answer to "nan == nan" is to raise an exception, because
you have asked a question for which the answer is nether True nor False.

    The correct semantics for IEEE floating point look something like
this:

	1/0 		INF
	INF + 1		INF
	INF - INF	NaN
	INF == INF	unordered
	NaN == NaN	unordered

INF and NaN both have comparison semantics which return
"unordered". The FPU sets a bit for this, which most language
implementations ignore.  But you can turn on floating point
exception traps, and on x86 machines, they're exact - the
exception will occur exactly at the instruction which
triggered the error.   In superscalar CPUs, a sizable part of
the CPU handles the unwinding necessary to do that.  x86 does
it, because it's carefully emulating non-superscalar machines.
Most RISC machines don't bother.

Python should raise an exception on unordered comparisons.
Given that the language handles integer overflow by going to
arbitrary-precision integers, checking the FPU status bits is
cheap.

The advantage of raising an exception is that the logical operations
still work.  For example,

	not (a == b)
	a != b

will always return the same results if exceptions are raised for
unordered comparison results.  Also, exactly one of

	a = b
	a < b
	a > b

is always true - something sorts tend to assume.

If you get an unordered comparison exception, your program
almost certainly was getting wrong answers.

(I used to do dynamics simulation engines, where this mattered.)

				John Nagle




More information about the Python-list mailing list