Here's a toy example in case of which Python exhibits behaviour that surprised me:
def harmonic_mean(x, y):
return 2/(1/x+1/y)
print(harmonic_mean(0.0, 1.0))
In IEEE-754 division by 0.0
shouldn't halt execution but rather it should just produce inf
(or -inf
or NaN
). Because of that, the above code should work even if one of the arguments is 0.0
. However, it seems to instead halt and print ZeroDivisionError: float division by zero
. Does that mean that Python doesn't follow IEEE-754 standard even on a platform that supports it? What's the motivation for making dividing by 0.0
cause a runtime error instead of producing a result that the standard prescribes? Do I have a way around that other than explicitly checking for zeros? I know that in this particular example I could instead e.g. use the following:
def harmonic_mean(x, y):
return 2*x*y/(x+y)
However, I would like to know what I can do more generally.
EDIT: I'll add that one reason I was surprised was that I had already seen NumPy behave mostly as I would have expected in a case of divide by zero. As an example, consider something like np.array([-1.0, 0.0, 1.0])/0.0
. The divide by zero causes a warning that reads RuntimeWarning: divide by zero encountered in divide
to be printed in the command line but the result is still [-inf nan inf]
as I would've expected.
From the IEEE 754-2019 standard:
exception: An event that occurs when an operation on some particular operands has no outcome suitable for every reasonable application. That operation might signal an exception by invoking default exception handling or alternate exception handling. Exception handling might signal further exceptions. Recognize that event, exception, and signal are defined in diverse ways in different programming environments.
In other words, while continuing execution as normal with an Inf
or NaN
result would be a compliant response to attempted division by zero, so would a non-local return, as is the case with Python.
The motivation for this is the assumption that a division of this sort is probably accidental, and that (all things considered) it would be better to immediately signal an error than to allow the result to cause more subtle errors later on. If this doesn't fit your needs, you can use numpy
for different semantics.
From The Zen of Python:
Errors should never pass silently.
Unless explicitly silenced.
To that point, '754 also recommends that implementations allow the user to modify the semantics of exception handling, such as to pass without error, log the error elsewhere, and/or substitute some arbitrary result. But this is not a requirement of the '754 standard: It uses the word "should" which it defines elsewhere as indicating that it is "particularly suitable" or "preferred but not necessarily required".