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

Andrew Barnert abarnert at yahoo.com
Fri Jan 9 06:38:14 CET 2015

On Wednesday, January 7, 2015 8:10 AM, Steven D'Aprano <steve at pearwood.info> wrote:

> Up to now, I have only discussed inf, not nan, because there is only one 
> inf value in floats. (Two if you count -inf.) But the IEEE 754 standard 
> provides many different NAN values, and offers a possible interpretion 
> of them: the extra "payload" on each NAN can be used to carry 
> diagnostic 
> information on which numeric operation failed. Back in the 1980s, I was 
> a user of the Apple Standard Numerics Environment (SANE), which provided 
> easy access to NAN payloads and a standard interpretation for them. It 
> is a system which can work well and help in debugging.
> 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. 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.

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. If you wanted the all-zeros NaN, that was a separate function.

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