[Python-Dev] Not-a-Number (was PyObject_RichCompareBool identity shortcut)

Mark Shannon marks at dcs.gla.ac.uk
Thu Apr 28 18:04:29 CEST 2011


Steven D'Aprano wrote:
> Mark Shannon wrote:
>> Related to the discussion on "Not a Number" can I point out a few things 
>> that have not be explicitly addressed so far.
>>
>> The IEEE standard is about hardware and bit patterns, rather than types 
>> and values so may not be entirely appropriate for high-level language
>> like Python.
> 
> I would argue that the implementation of NANs is irrelevant. If NANs are 
> useful in hardware floats -- and I think they are -- then they're just 
> as equally useful as objects, or as strings in languages like REXX or 
> Hypertalk where all data is stored as strings, or as quantum wave 
> functions in some future quantum computer.

So,
Indeed, so its OK if type(NaN) != type(0.0) ?

> 
> 
>> NaN is *not* a number (the clue is in the name).
>> Python treats it as if it were a number:
>>
>>  >>> import numbers
>>  >>> isinstance(nan, numbers.Number)
>> True
>>
>> Can be read as "'Not a Number' is a Number" ;)
> 
> I see your wink, but what do you make of these?
> 
> class NotAnObject(object):
>      pass
> 
> nao = NotAnObject()
> assert isinstance(nao, object)

Trying to make something not an object in a language where everything is 
an object is bound to be problematic.

> 
> class NotAType(object):
>      pass
> 
> assert type(NotAType) is type
> 
> 
> 
>> NaN does not have to be a float or a Decimal.
>> Perhaps it should have its own class.
> 
> Others have already pointed out this won't make any difference.
> 
> Fundamentally, the problem is that some containers bypass equality tests 
> for identity tests. There may be good reasons for that shortcut, but it 
> leads to problems with *any* object that does not define equality to be 
> reflexive, not just NANs.
> 
> 
>  >>> class Null:
> ...     def __eq__(self, other):
> ...             return False
> ...
>  >>> null = Null()
>  >>> null == null
> False
>  >>> [null] == [null]
> True
> 

Just because you can do that, doesn't mean you should.
Equality should be reflexive, without that fundamental assumption many 
non-numeric algorithms fall apart.

> 
> 
>> The default comparisons will then work as expected for collections.
>> (No doubt, making NaN a new class will cause a whole new set of problems)
>>
>> As pointed out by Meyer:
>> NaN == NaN is False
>> is no more logical than
>> NaN != NaN is False
> 
> I don't agree with this argument. I think Meyer is completely mistaken 
> there. The question of NAN equality is that of a vacuous truth, quite 
> similar to the Present King of France:
> 
> http://en.wikipedia.org/wiki/Present_King_of_France
> 
> Meyer would have us accept that:
> 
>      The present King of France is a talking horse
> 
> and
> 
>      The present King of France is not a talking horse
> 
> are equally (pun not intended) valid. No, no they're not. I don't know 
> much about who the King of France would be if France had a king, but I 
> do know that he wouldn't be a talking horse.
> 
> Once you accept that NANs aren't equal to anything else, it becomes a 
> matter of *practicality beats purity* to accept that they can't be equal 

Not breaking a whole bunch of collections and algorithms has a certain 
practical appeal as well ;)

> to themselves either. A NAN doesn't represent a specific thing. It's a 
> signal that your calculation has generated an indefinite, undefined, 
> undetermined value. NANs aren't equal to anything. The fact that a NAN 
> happens to have an existence as a bit-pattern at some location, or as a 
> distinct object, is an implementation detail that is irrelevant. If you 
> just happen by some fluke to compare a NAN to "itself", that shouldn't 
> change the result of the comparison:
> 
>      The present King of France is the current male sovereign who
>      rules France
> 
> is still false, even if you happen to write it like this:
> 
>      The present King of France is the present King of France
> 

The problem with this argument is the present King of France does not 
exist, whereas NaN (as a Python object) does exist.

The present King of France argument only applies to non-existent things. 
Python objects do exist (as much as any computer language entity 
exists). So the expression "The present King of France" either raises an 
exception (non-existence) or evaluates to an object (existence).
In this case "the present King of France" doesn't exist and should raise 
a FifthRepublicException :)
inf / inf does not raise an exception, but evaluates to NaN, so NaN
exists. For objects (that exist):
(x is x) is True.
The present President of France is the present President of France,
regardless of who he or she may be.
> 
> This might seem surprising to those who are used to reflexivity. Oh 
> well. Just because reflexivity holds for actual things, doesn't mean it 
> holds for, er, things that aren't things. NANs are things that aren't 
> things.
A NaN is thing that *is* a thing; it exists:
object.__repr__(float('nan'))
> 

Of course if inf - inf, inf/inf raised exceptions,
then NaN wouldn't exist (as a Python object)
and the problem would just go away :)
After all 0.0/0.0 already raises an exception, but the
IEEE defines 0.0/0.0  as NaN.
> 
>> Although both NaN == NaN and NaN != NaN could arguably be a "maybe" 
>> value, the all important reflexivity (x == x is True)  is effectively 
>> part of the language.
>> All collections rely on it and Python wouldn't be much use without 
>> dicts, tuples and lists.
> 
> Perhaps they shouldn't rely on it. Identity tests are an implementation 
> detail. But in any case, reflexivity is *not* a guarantee of Python. 
> With rich comparisons, you can define __eq__ to do anything you like.

And if you do define __eq__ to be non-reflexive then things will break.
Should an object that breaks so much (ie NaN in its current form) be in 
the standard library?
Perhaps we should just get rid of it?




More information about the Python-Dev mailing list