Question about `float('nan')`

An OT consideration: if you want a nan singleton using only vanilla Python, just use math.nan:

>>> from math import nan
>>> nan is nan
True
>>> nan == nan
False

I bet the 99% of the people – me included – will use it only because it’s boring to write float("nan") :smiley:

I don’t think in this way you get a singleton, in a strict sense.

That’s just reflexivity of the is operation. Same true for any object:

>>> boo = object()
>>> boo is boo
True

Your nan won’t be unique, just as shown above — arithmetic operations or mathematical functions will turn it to some other object with a different identity:

>>> import math
>>> from math import nan
>>> -nan
nan
>>> nan is -nan  # here it can't be same object, per IEEE 754
False
>>> math.sin(nan) is nan
False

? So? Also -True is not True is True. I don’t get the point.

Of course

float("nan") is math.nan

if False. But

float(math.nan) is math.nan

is True.

Not a textbook singleton? Probably. But there’s something better in vanilla Python? :wink:

The point is that you don’t get a singleton in this way. All you shown us is just a reflexivity property for a particular Python object. But that holds for any object, not just for a particular math.nan.

There can’t be “just one nan”. At least -nan must be a distinct object.

And the significance of this is that if x is nan: will never be a safe way to test for nan. Using math.nan might be more convenient than float("nan") when you’re assigning to it, but it’s no help with comparisons. The correct way to test for nanness is to see if it’s not equal to itself, and that doesn’t need anything from the math module.

And that manifests us, that it’s not a true singleton object.

Assuming you know type of the tested variable. If it’s not a float, well, nothing prevent us from inventing some other object with silly property wrt to equality (breaking reflexivity). Mathematica has, for example, the Indeterminate object, like a NaN in some sense:

In[1]:= Needs["ComputerArithmetic`"]                                                                                                                           
In[2]:= NaN == NaN                                                                                                                                             
Out[2]= False
In[3]:= NaN === NaN                                                                                                                                            
Out[3]= True
In[4]:= Indeterminate                                                                                                                                          
Out[4]= Indeterminate
In[5]:= Indeterminate == Indeterminate                                                                                                                         
Out[5]= Indeterminate == Indeterminate
In[6]:= Indeterminate === Indeterminate                                                                                                                        
Out[6]= True
In[7]:= TrueQ[Indeterminate == Indeterminate]                                                                                                                  
Out[7]= False

That’s true, but I would argue that the IEEE floating point definition is so well-known at this point that anyone who’s making an object that isn’t equal to itself is deliberately evoking nan-like behaviour. Notably, the Indeterminate docs stipulates that, just like with NaN, operations on Indeterminate produce Indeterminate, and also like NaN, it represents not a single value but all possible values. So if you pass this to a naive “isnan()” function and it returns True, I would say that it’s not incorrect.

Because

float("nan") is math.nan

is False?

If so, who cares? :slight_smile: I mean, usually singleton are used for speeding up the application.

I suppose it’s simple to change the float constructor to return a singleton and use pyperformance to see if there’s any speedup.

Of course. You can create mymodule and put there x = MyClass() and use it in your app as a single source of truth.

Is it not a book singleton? Maybe. But AFAIK people usually call it a “singleton” :wink:

I’d say that it’s much more common for singletons to be used to ensure something, such as making sure there’s only one event loop. It isn’t just a performance question.

But there’s a lot of dispute about the true meaning of that word, and whether “globally unique instance” is the same thing, or if it only counts when the constructor returns the same instance again.

Good point! It’s also good for concurrency, for having a single source of truth etc.

I fail to see the usefulness for float("nan") in these contexts. But maybe I’ve not the entire picture :face_with_raised_eyebrow:

Yeah. Far as I know, the only reason to use it is because math.nan is more convenient than float("nan").

Lets ignore now, that this doesn’t play nicely with the standard.

Then yes, you can do that. Just remember, that underlying hardware arithmetic has different NaN’s and this affects results of arithmetic operations and libm functions.

You have to “canonicalize” hardware NaN’s somehow, say pick positive quiet NaN with no payload. The repr() behavior for CPython’s float’s may give you an illusion, that it’s already the case:

>>> float('nan')
nan
>>> -_  # no visual difference
nan

Yet you can reveal the difference, using some library functions like math.copysign() or unpacking values by the struct module. BTW, the Decimal’s play here more nicely:

>>> import decimal
>>> ctx = decimal.getcontext()
>>> decimal.Decimal('NaN123')  # payload shown
Decimal('NaN123')
>>> ctx.is_nan(_)
True

I doubt, that you will see anything. Benchmarks here (like nbody) are sensitive to normal floating-point numbers.

BTW, float('inf') and float('-inf') aren’t singletons either.