[Python-ideas] math.inf and math.nan constants

Steven D'Aprano steve at pearwood.info
Sat Jan 10 09:33:05 CET 2015

On Fri, Jan 09, 2015 at 05:38:14AM +0000, Andrew Barnert wrote:
> On Wednesday, January 7, 2015 8:10 AM, Steven D'Aprano <steve at pearwood.info> wrote:
> > Python today doesn't give us any simple way to access all those 
> > different float NANs, or offer any interpretation of what they might 
> > mean, but it might some day. I am strongly opposed to anything which 
> > might pre-empt that or discourage future advances.
> I don't think there's really an issue here; the only thing this 
> preempts is using the plain name `float.nan` for such future advances. 

Okay, okay, I admit defeat :-)

I must say, compared to some years ago when the prevailing attitude 
about NANs was closer to "if Python supports them at all you're lucky", 
I am glad to see better support for NANs. If that means baking in the 
idea that there is "a NAN", I can live with that.

> What's wrong with, say, `float.make_nan(payload=30, quiet=False, 
> sign=True)`? Given that the former is almost certainly going to be 
> used far more often than the latter, the extra characters to type and 
> read for the uncommon case don't seem like a serious problem.

Or float('-snan30').

> Since you brought up SANE, from what I remember (I tried to dig up the 
> Apple Numerics Manual or the semi-public SANE 68k assembly source, but 
> I give up…), this is roughly equivalent to what SANE did. There was a 
> function to return a NaN with a code from 0-255, with meanings like 
> "square root of a negative" or "division by zero" or "invalid NaN 
> code"—the last being used if you tried to call it with 0 as the code. 

The payloads SANE used (adjusted to Python syntax where appropriate):

Name		Payload		Example
=============	=======		=========
NANSQRT		1		sqrt(-1)
NANADD		2		+INF + (-INF)
NANDIV		4		0/0
NANREM		9		x % 0
NANASCBIN	17		float("foo")
NANCOMP		20		convert COMP NAN to float
NANZERO		21		create NAN with 0 payload
NANTRIG		33		invalid arg to trig functions
NANINVTRIG	34		asin(2.0)
NANLOG		36		log(-1)
NANPOWER	37		invalid arg to x**y
NANFINAN	38		invalid arg to finance funcs

> If you wanted the all-zeros NaN, that was a separate function.

I don't think there is an all-zeroes NAN for single and double format. I 
believe they would be +INF or -INF depending on the sign bit.

There was a 64-bit "comp" data type which had a single NAN value, 
perhaps that is what you are thinking of. comp was integer valued, and 
had no INFs.

> Meanwhile, if you only care about platforms that use x86/68k-style 
> representations for IEEE-754 (unfortunately, pre-2008 the bits weren't 
> standardized, so at least MIPS did things differently), `make_nan` is 
> pretty easy. Something like:
>     def make_nan(payload=0, sign=False, quiet=True):
>         payload |= (0x7ff0000000000000 | 
>                     (sign * 0x8000000000000000) | 
>                     (quiet *  0x0008000000000000))
>         return struct.unpack('d', struct.pack('Q', payload))[0]


> And likewise, you could write a `parse_nan` that returns `payload, 
> sign, quiet`. And you'd probably want to add some parameter validation 
> (and don't forget that payload 0 and quiet-bit 0 means it's not a NaN, 
> it's an inf).


More information about the Python-ideas mailing list