math.hypot, complex.__abs__, and documentation

I have a minor concern about certain corner cases with math.hypot and complex.__abs__, namely when one component is infinite and one is not a number. If we execute the following code: import math inf = float('inf') nan = float('nan') print math.hypot(inf, nan) print abs(complex(nan, inf)) ... then we see that 'inf' is printed in both cases. The standard library tests (for example, test_cmath.py:test_abs()) seem to test for this behavior as well, and FWIW, I personally agree with this convention. However, the math module's documentation for both 2.6 and 3.1 states, "All functions return a quiet NaN if at least one of the args is NaN." math.pow(1.0, nan) is another such exception to the rule. Perhaps the documentation should be updated to reflect this. Thanks, - David

David DiCato wrote:
… then we see that ‘inf’ is printed in both cases. The standard library tests (for example, test_cmath.py:test_abs()) seem to test for this behavior as well, and FWIW, I personally agree with this convention. However, the math module’s documentation for both 2.6 and 3.1 states, “All functions return a quiet NaN if at least one of the args is NaN.”
math.pow(1.0, nan) is another such exception to the rule. Perhaps the documentation should be updated to reflect this.
This sounds like a legitimate documentation bug for the tracker at bugs.python.org (bug reports tend to get lost/forgotten if they only exist on the mailing list). Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia ---------------------------------------------------------------

On Tue, Feb 16, 2010 at 9:19 PM, David DiCato <ddicato@microsoft.com> wrote:
I have a minor concern about certain corner cases with math.hypot and complex.__abs__, namely when one component is infinite and one is not a number.
<examples snipped>
as well, and FWIW, I personally agree with this convention. However, the math module’s documentation for both 2.6 and 3.1 states, “All functions return a quiet NaN if at least one of the args is NaN.”
Yes; this is a doc bug. Please could you open an issue on http://bugs.python.org ?
math.pow(1.0, nan) is another such exception to the rule. Perhaps the documentation should be updated to reflect this.
Yes, it should. Thanks! Mark

Ok, thanks! It's submitted as issue 7947. - David -----Original Message----- From: Mark Dickinson [mailto:dickinsm@gmail.com] Sent: Tuesday, February 16, 2010 2:15 PM To: David DiCato Cc: python-dev@python.org Subject: Re: [Python-Dev] math.hypot, complex.__abs__, and documentation On Tue, Feb 16, 2010 at 9:19 PM, David DiCato <ddicato@microsoft.com> wrote:
I have a minor concern about certain corner cases with math.hypot and complex.__abs__, namely when one component is infinite and one is not a number.
<examples snipped>
as well, and FWIW, I personally agree with this convention. However, the math module’s documentation for both 2.6 and 3.1 states, “All functions return a quiet NaN if at least one of the args is NaN.”
Yes; this is a doc bug. Please could you open an issue on http://bugs.python.org ?
math.pow(1.0, nan) is another such exception to the rule. Perhaps the documentation should be updated to reflect this.
Yes, it should. Thanks! Mark

On Wed, 17 Feb 2010 08:19:00 am David DiCato wrote:
I have a minor concern about certain corner cases with math.hypot and complex.__abs__, namely when one component is infinite and one is not a number. If we execute the following code:
import math inf = float('inf') nan = float('nan') print math.hypot(inf, nan) print abs(complex(nan, inf))
... then we see that 'inf' is printed in both cases. The standard library tests (for example, test_cmath.py:test_abs()) seem to test for this behavior as well, and FWIW, I personally agree with this convention.
What's the justification for that convention? It seems wrong to me. If you expand out hypot and substitute a=inf and b=nan, you get:
math.sqrt(inf*inf + nan*nan) nan
which agrees with my pencil-and-paper calculation: sqrt(inf*inf + nan*nan) = sqrt(inf + nan) = sqrt(nan) = nan -- Steven D'Aprano

Mathematically, think of nan as 'indeterminate'. When you're trying to get the magnitude of a vector, you know that it's infinite if even one of the components is infinite. So, the fact that the other component is indeterminate can be ignored. It's the same with math.pow(1.0, float('nan')); the second argument simply doesn't matter when the first is 1.0. FWIW, these conventions also exist in the C99 standard. Hope this helps, - David -----Original Message----- From: python-dev-bounces+ddicato=microsoft.com@python.org [mailto:python-dev-bounces+ddicato=microsoft.com@python.org] On Behalf Of Steven D'Aprano Sent: Tuesday, February 16, 2010 2:47 PM To: python-dev@python.org Subject: Re: [Python-Dev] math.hypot, complex.__abs__, and documentation On Wed, 17 Feb 2010 08:19:00 am David DiCato wrote:
I have a minor concern about certain corner cases with math.hypot and complex.__abs__, namely when one component is infinite and one is not a number. If we execute the following code:
import math inf = float('inf') nan = float('nan') print math.hypot(inf, nan) print abs(complex(nan, inf))
... then we see that 'inf' is printed in both cases. The standard library tests (for example, test_cmath.py:test_abs()) seem to test for this behavior as well, and FWIW, I personally agree with this convention.
What's the justification for that convention? It seems wrong to me. If you expand out hypot and substitute a=inf and b=nan, you get:
math.sqrt(inf*inf + nan*nan) nan
which agrees with my pencil-and-paper calculation: sqrt(inf*inf + nan*nan) = sqrt(inf + nan) = sqrt(nan) = nan -- Steven D'Aprano _______________________________________________ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/ddicato%40microsoft.com

On Tue, Feb 16, 2010 at 10:46 PM, Steven D'Aprano <steve@pearwood.info>
What's the justification for that convention? It seems wrong to me.
It's difficult to do better than to point to Kahan's writings. See http://www.eecs.berkeley.edu/~wkahan/ieee754status/IEEE754.PDF and particularly the discussion on page 8 that starts "Were there no way to get rid of NaNs ...". I don't think it covers hypot, but the same justification given for having nan**0 == 1 applies here. Interestingly, he says that at the time of writing, 1**nan == nan is the preferred alternative. But since then, the standards (well, at least C99 and IEEE 754-2008) have come out in favour of 1**nan == 1. Mark

On Tue, Feb 16, 2010 at 11:06 PM, Mark Dickinson <dickinsm@gmail.com> wrote:
and particularly the discussion on page 8 that starts "Were there no way to get rid of NaNs ...". I don't think it covers hypot, but the
Whoops. I should have reread that article myself. The behaviour of hypot *is* mentioned, on page 7. Mark

On Wed, 17 Feb 2010 10:06:01 am Mark Dickinson wrote:
On Tue, Feb 16, 2010 at 10:46 PM, Steven D'Aprano <steve@pearwood.info>
What's the justification for that convention? It seems wrong to me.
It's difficult to do better than to point to Kahan's writings. See
http://www.eecs.berkeley.edu/~wkahan/ieee754status/IEEE754.PDF
Well, who am I to question Kahan? I guess if you interpret nan as "indeterminate", than hypot(inf, nan) should be inf; but if you interpret it as "not a number", then it should be nan. Since NANs can be both, I guess we're stuck with one or the other. So I'm satisfied that there's a good reason for the behaviour, even if I'm not 100% convinced it's the best reason. On a related note, why the distinction here?
inf*inf inf inf**2 Traceback (most recent call last): File "<stdin>", line 1, in <module> OverflowError: (34, 'Numerical result out of range')
-- Steven D'Aprano

[With apologies for Steven for the duplicate email.] On Wed, Feb 17, 2010 at 12:22 PM, Steven D'Aprano <steve@pearwood.info> wrote:
Well, who am I to question Kahan?
Yes, there I go with the argument from authority. But while we shouldn't instantly accept Kahan's arguments just because he's Kahan, it would be equally foolish for us mere mortals to ignore words from one of the prime movers of the IEEE 754 standard. :-)
I guess if you interpret nan as "indeterminate", than hypot(inf, nan) should be inf; but if you interpret it as "not a number", then it should be nan. Since NANs can be both, I guess we're stuck with one or the other.
Apart from the 'should be's, I think there's also a practical aspect to consider: I'm guessing that part of the reason for this sort of behaviour is that it make it more likely for numerical code to 'do the right thing' without extra special-case handling, in much the same way that infinities can appear and disappear during a numerical calculation, leaving a valid finite result, without the user having had to worry about inserting special cases to handle those infinities. As an example of the latter behaviour, consider evaluating the function f(x) = 1/(1+1/x) naively at x = 0; if this formula appears in any real-world circumstances, the chances are that you want a result of 0, and IEEE 754's non-stop mode gives it to you. (This doesn't work in Python, of course, because it doesn't really have a non-stop mode; more on this below.) Unfortunately, to back this argument up properly I'd need lots of real-world examples, which I don't have. :(
So I'm satisfied that there's a good reason for the behaviour, even if I'm not 100% convinced it's the best reason.
From Python's point of view, the real reason for implementing it this way is that it follows current standards (C99 and IEEE 754; probably also the Fortran standards too, but I haven't checked), so this special case behaviour (a) likely matches expectations for numerical users, and (b) has been thought about carefully by at least some experts.
On a related note, why the distinction here?
inf*inf inf inf**2 Traceback (most recent call last): File "<stdin>", line 1, in <module> OverflowError: (34, 'Numerical result out of range')
For that particular example, it's because you haven't upgraded to Python 2.7 yet. :) Python 2.7a3+ (trunk:78206M, Feb 17 2010, 10:19:00) [GCC 4.2.1 (Apple Inc. build 5646) (dot 1)] on darwin Type "help", "copyright", "credits" or "license" for more information.
float('inf') ** 2 inf
See http://bugs.python.org/issue7534. But there are similar problems that aren't fixed, and can't reasonably be fixed without causing upset:
1e300 ** 2 Traceback (most recent call last): File "<stdin>", line 1, in <module> OverflowError: (34, 'Result too large') 1e300 * 1e300 inf
Here I'd argue that the ideal Python behaviour would be to produce an OverflowError in both cases; more generally, arithmetic with finite numbers would never produce infinities or nans, but always raise Python exceptions instead. But some users need or expect some kind of 'non-stop mode' for arithmetic, so changing this probably wouldn't go down well. Mark
participants (4)
-
David DiCato
-
Mark Dickinson
-
Nick Coghlan
-
Steven D'Aprano