
On Fri, Apr 29, 2011 at 4:23 PM, Robert Kern <robert.kern@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.