float("nan") in set or as key

Chris Torek nospam at torek.net
Sun Jun 5 21:56:51 EDT 2011


>> On Mon, Jun 6, 2011 at 8:54 AM, Chris Torek <nospam at torek.net> wrote:
>>> A signalling NaN traps at (more or less -- details vary depending on
>>> FPU architecture) load time.

>On Mon, 06 Jun 2011 09:13:25 +1000, Chris Angelico wrote:
>> Load. By this you mean the operation of taking a bit-pattern in RAM and
>> putting it into a register? So, you can calculate 0/0, get a signalling
>> NaN, and then save that into a memory variable, all without it trapping;
>> and then it traps when you next perform an operation on that number?

I mean, if you think of the FPU as working (in principle) with
either just one or two registers and a load/store architecture, or
a tiny little FPU-stack (the latter is in fact the case for Intel
FPUs), with no optimization, you get a trap when you attempted to
load-up the sNaN value in order to do some operation on it.  For
instance, if x is an sNaN, "y = x + 1" turns into "load x; load
1.0; add; store y" and the trap occurs when you do "load x".

In article <4dec2ba6$0$29996$c3e8da3$5496439d at news.astraweb.com>,
Steven D'Aprano  <steve+comp.lang.python at pearwood.info> wrote:
>The intended behaviour is operations on "quiet NANs" should return NANs, 
>but operations on "signalling NANs" should cause a trap, which can either 
>be ignored, and converted into a quiet NAN, or treated as an exception.
>
>E.g. in Decimal:
>
>>>> import decimal
>>>> qnan = decimal.Decimal('nan')  # quiet NAN
>>>> snan = decimal.Decimal('snan')  # signalling NAN
>>>> 1 + qnan
>Decimal('NaN')
>>>> 1 + snan
>Traceback (most recent call last):
>  File "<stdin>", line 1, in <module>
>  File "/usr/local/lib/python3.1/decimal.py", line 1108, in __add__
>    ans = self._check_nans(other, context)
>  File "/usr/local/lib/python3.1/decimal.py", line 746, in _check_nans
>    self)
>  File "/usr/local/lib/python3.1/decimal.py", line 3812, in _raise_error
>    raise error(explanation)
>decimal.InvalidOperation: sNaN

Moreover:

   >>> cx = decimal.getcontext()
   >>> cx
   Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999999, Emax=999999999, capitals=1, flags=[], traps=[DivisionByZero, Overflow, InvalidOperation])
   >>> cx.traps[decimal.InvalidOperation] = False
   >>> snan
   Decimal("sNaN")
   >>> 1 + snan
   Decimal("NaN")

so as you can see, by ignoring the InvalidOperation exception, we
had our sNaN converted to a (regular, non-signal-ing, "quiet") NaN,
and 1 + NaN is still NaN.

(I admit that my mental model using "loads" can mislead a bit since:

    >>> cx.traps[decimal.InvalidOperation] = True # restore trapping
    >>> also_snan = snan
    >>>

A simple copy operation is not a "load" in this particular sense,
and on most real hardware, one just uses an ordinary 64-bit integer
memory-copying operation to copy FP bit patterns from one place to
another.)

There is some good information on wikipedia:

    http://en.wikipedia.org/wiki/NaN

(Until I read this, I was not aware that IEEE now recommends that
the quiet-vs-signal bit be 1-for-quiet 0-for-signal. I prefer the
other way around since you can then set memory to all-1-bits if it
contains floating point numbers, and get exceptions if you refer
to a value before seting it.)
-- 
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W)  +1 801 277 2603
email: gmail (figure it out)      http://web.torek.net/torek/index.html



More information about the Python-list mailing list