[Python-ideas] Disallow orderring comparison to NaN
Alexander Belopolsky
alexander.belopolsky at gmail.com
Fri Apr 29 23:30:56 CEST 2011
On Fri, Apr 29, 2011 at 4:23 PM, Robert Kern <robert.kern at gmail.com> wrote:
..
> (Section 7. "Exceptions") "The default response to an exception shall be to
> proceed without a trap."
I cannot find this phrase in my copy of IEEE Std 754-2008. Instead, I
see the following in section 7.1:
"This clause also specifies default non-stop exception handling for
exception signals, which is to deliver a default result, continue
execution, and raise the corresponding status flag."
As I mentioned before, Python does not have a mechanism that would
allow to simultaneously raise an exception and deliver the result. We
have to choose one or the other. I think the choice made in the
decimal module is a reasonable one: trap Overflow, DivisionByZero, and
InvalidOperation while ignoring Underflow and Inexact.
The choices made for float operations are more ad-hoc: DivisionByZero
is always trapped:
>>> 1.0/0.0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: float division by zero
>>> math.log(0.0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: math domain error
Overflow is trapped in some cases:
>>> 1e308 ** 2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OverflowError: (34, 'Result too large')
>>> math.exp(1e20)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OverflowError: math range error
and ignored in others:
>>> 1e308+ 1e308
inf
>>> 1e200 * 1e200
inf
InvalidOperation is not handled consistently. Let me copy the
relevant section of the standard and show Python's behavior for each
case where InvalidOperation exception is required by the standard:
"""
The invalid operation exception is signaled if and only if there is no
usefully definable result. In these cases the operands are invalid for
the operation to be performed.
For operations producing results in floating-point format, the default
result of an operation that signals the invalid operation exception
shall be a quiet NaN that should provide some diagnostic information
(see 6.2).
These operations are:
a) any general-computational or signaling-computational operation on a
signaling NaN (see 6.2), except for some conversions (see 5.12)
"""
Python does not have support for sNaNs. It is possible to produce a
float carrying an sNaN using struct.unpack, but the result behaves a
qNaN. InvalidOperation not trapped.
"""
b) multiplication: multiplication(0, ∞) or multiplication(∞, 0)
"""
>>> 0.0 * float('inf')
nan
InvalidOperation not trapped.
"""
c) fusedMultiplyAdd: fusedMultiplyAdd(0, ∞, c) or fusedMultiplyAdd(∞,
0, c) unless c is a quiet NaN; if c is a quiet NaN then it is
implementation defined whether the invalid operation exception is
signaled
"""
Not applicable. Python does not have fusedMultiplyAdd (x * y + z) function.
"""
d) addition or subtraction or fusedMultiplyAdd: magnitude subtraction
of infinities, such as: addition(+∞, −∞)
"""
>>> float('inf') + float('-inf')
nan
InvalidOperation not trapped.
"""
e) division: division(0, 0) or division(∞, ∞)
"""
>>> 0.0/0.0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: float division by zero
InvalidOperation trapped, but misreported as DivisionByZero.
>>> float('inf') / float('inf')
nan
"""
f) remainder: remainder(x, y), when y is zero or x is infinite and
neither is NaN
"""
>>> 1.0 % 0.0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: float modulo
InvalidOperation trapped, but misreported as DivisionByZero.
>>> float('inf') % 2.0
nan
InvalidOperation not trapped.
"""
g) squareRoot if the operand is less than zero
"""
>>> math.sqrt(-1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: math domain error
InvalidOperation trapped.
"""
h) quantize when the result does not fit in the destination format or
when one operand is finite and the other is infinite
"""
Not applicable.
"""
For operations producing no result in floating-point format, the
operations that signal the invalid operation exception are:
i) conversion of a floating-point number to an integer format, when
the source is NaN, infinity, or a value that would convert to an
integer outside the range of the result format under the applicable
rounding attribute.
"""
>>> int(float('nan'))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: cannot convert float NaN to integer
InvalidOperation trapped.
>>> int(float('inf'))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OverflowError: cannot convert float infinity to integer
InvalidOperation trapped, but misclassified as OverflowError.
"""
j) comparison by way of unordered-signaling predicates listed in Table
5.2, when the operands are unordered
"""
This is the subject of my proposal.
>>> float('nan') < 0.0
False
InvalidOperation not trapped.
"""
k) logB(NaN), logB(∞), or logB(0) when logBFormat is an integer format
(see 5.3.3).
"""
Not applicable.
Overall, it appears that in cases where InvalidOperation was
anticipated, it was converted to some type of exception in Python.
Exceptions to this rule seem to be an accident of implementation.
More information about the Python-ideas
mailing list