float("nan") in set or as key
Steven D'Aprano
steve+comp.lang.python at pearwood.info
Mon Jun 6 00:59:11 EDT 2011
On Mon, 06 Jun 2011 14:11:03 +1000, Chris Angelico wrote:
> On Mon, Jun 6, 2011 at 11:21 AM, 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: [snip]
>
> So does this mean that:
>
> a = 0.0/0.0
> b = a + 1
>
> (with signalling NANs) should trap on the second line but not the first?
> That's the first "operation on a nan".
Sort of.
Firstly, in order for a = 0.0/0.0 to not trap (not raise an exception),
you have to tell it not to trap InvalidOperation (and DivideByZero I
think?). So using Decimal:
>>> import decimal
>>> decimal.getcontext()
Context(prec=9, rounding=ROUND_HALF_UP, Emin=-999999999, Emax=999999999,
capitals=1, flags=[], traps=[Underflow, Clamped, DivisionByZero,
Overflow, InvalidOperation])
If we call Decimal(0)/Decimal(0), it will be trapped, which is treated as
an exception in Python. To get a NAN:
>>> decimal.setcontext(decimal.ExtendedContext)
>>> decimal.getcontext()
Context(prec=9, rounding=ROUND_HALF_EVEN, Emin=-999999999,
Emax=999999999, capitals=1, flags=[], traps=[])
>>>
>>> D = decimal.Decimal
>>> a = D(0)/D(0)
>>> a
Decimal('NaN')
Note that a flag is set, so you can tell that an exceptional event has
occurred:
>>> decimal.getcontext()
Context(prec=9, rounding=ROUND_HALF_EVEN, Emin=-999999999,
Emax=999999999, capitals=1, flags=[InvalidOperation], traps=[])
But the NAN given is a quiet NAN. Doing further operations on it doesn't
trap:
>>> decimal.getcontext().traps[decimal.InvalidOperation] = 1
>>> decimal.getcontext().flags.clear()
>>> b = a + 1
>>> decimal.getcontext()
Context(prec=9, rounding=ROUND_HALF_EVEN, Emin=-999999999,
Emax=999999999, capitals=1, flags=[], traps=[InvalidOperation])
However, if you use a signalling NAN, the situation is different. As far
as I can tell, the only way to get a signalling NAN is to create one
yourself:
>>> c = D('sNAN')
>>> d = c + 1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib/python2.6/decimal.py", line 1064, in __add__
ans = self._check_nans(other, context)
File "/usr/local/lib/python2.6/decimal.py", line 703, in _check_nans
self)
File "/usr/local/lib/python2.6/decimal.py", line 3778, in _raise_error
raise error(explanation)
decimal.InvalidOperation: sNaN
I don't think that there's any way to tell IEEE-754 for operations on
NANs to return signalling NANs. As I understand it, the idea is:
- if you want exceptions to signal, set the appropriate traps;
- if you want NANs that propagate through your calculation, clear the
traps and you'll get propagating NANs;
- if you need to detect the presence of a NAN in your calculation, you
can inspect the flags at any time and take whatever action you want;
- and if you want a signalling NAN, you have to inject it yourself into
your calculation, and then avoid using it.
I'm lead to believe that signalling NANs were added to satisfy politics,
but apart from being slightly useful for marking uninitialised memory
before use, nobody actually uses them in practice.
Wanna see something cool? You can check for inexact arithmetic:
>>> decimal.getcontext().flags
{<class 'decimal.InvalidOperation'>: 1}
>>> D(1)/D(7)
Decimal('0.142857143')
>>> decimal.getcontext().flags
{<class 'decimal.Inexact'>: 1, <class 'decimal.InvalidOperation'>: 1,
<class 'decimal.Rounded'>: 1}
and trap on it:
>>> decimal.getcontext().traps[decimal.Inexact] = 1
>>> D(1)/D(7)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib/python2.6/decimal.py", line 1275, in __truediv__
return ans._fix(context)
File "/usr/local/lib/python2.6/decimal.py", line 1632, in _fix
context._raise_error(Inexact)
File "/usr/local/lib/python2.6/decimal.py", line 3778, in _raise_error
raise error(explanation)
decimal.Inexact: None
Not surprisingly, by default that's turned off :)
--
Steven
More information about the Python-list
mailing list