classification
Title: add inf, nan, infj, nanj to cmath module
Type: enhancement Stage: resolved
Components: Extension Modules Versions: Python 3.6
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: mark.dickinson Nosy List: haypo, mark.dickinson, pitrou, python-dev, serhiy.storchaka
Priority: normal Keywords: patch

Created on 2015-01-13 09:37 by mark.dickinson, last changed 2016-08-29 12:59 by mark.dickinson. This issue is now closed.

Files
File name Uploaded Description Edit
cmath_inf_nan.patch mark.dickinson, 2016-08-21 19:33 review
Messages (23)
msg233919 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2015-01-13 09:37
As pointed out by Guido in issue 23185, the constants `inf` and `nan` should be added to the cmath module, too.
msg233921 - (view) Author: STINNER Victor (haypo) * (Python committer) Date: 2015-01-13 09:37
What about cmath.nanj?
msg233922 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2015-01-13 09:38
Guido also says:

"""
Also complex(0, math.nan) a value that is printed as "nanj" and complex("nanj") parses and returns such a value, so the point could be made that there should be a constant named complex.nanj.
"""

... and the same comments would apply to "infj".
msg233923 - (view) Author: STINNER Victor (haypo) * (Python committer) Date: 2015-01-13 09:38
Oh, there is also infj:

>>> complex(0, float("inf"))
infj
>>> complex("infj")
infj
>>> complex(0, float("nan"))
nanj
>>> complex("nanj")
nanj
msg233924 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2015-01-13 09:39
@haypo: I'm not keen on either of infj or nanj, on a YAGNI basis.  I expect they'd be used almost never, and for the few times that they're really needed, complex(0, inf) and complex(0, nan) seem like good enough spellings.
msg233925 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2015-01-13 09:56
Note: following the precedent of cmath.e and cmath.pi, cmath.nan and cmath.inf should have type *float*.  Let's not get into the business of deciding *which* complex infinities and nans to represent.
msg233926 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2015-01-13 09:59
There are other names which exist only in math, but not in cmath.

>>> sorted(set(dir(math)) - set(dir(cmath)))
['atan2', 'ceil', 'copysign', 'degrees', 'erf', 'erfc', 'expm1', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'fsum', 'gamma', 'hypot', 'inf', 'ldexp', 'lgamma', 'log1p', 'log2', 'modf', 'nan', 'pow', 'radians', 'trunc']

May be complex equivalents of all functions should be added for the same reasons?
msg233927 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2015-01-13 10:21
> May be complex equivalents of all functions should be added for the same reasons?

(1) This is off-topic for this issue; please open a separate one.

(2) Many of those functions simply don't make sense for complex numbers (for example floor, degrees, etc.), or it's not obvious how to extend them.  For those that do make sense (erf, for example), implementing and testing a library-quality version would take a lot of time and effort, and no-one would be interested in the result.  It's not worth it.
msg233949 - (view) Author: Guido van Rossum (gvanrossum) * (Python committer) Date: 2015-01-13 16:46
> Note: following the precedent of cmath.e and cmath.pi, cmath.nan and
cmath.inf should have type *float*.  Let's not get into the business of
deciding *which* complex infinities and nans to represent.

Agreed.
msg233950 - (view) Author: Guido van Rossum (gvanrossum) * (Python committer) Date: 2015-01-13 16:53
Note, the reason I proposed nanj (and infj) is that these are produced by
repr() of complex numbers. Having them in the cmath module provides a place
to document them which will then be searchable.

On Tue, Jan 13, 2015 at 8:46 AM, Guido van Rossum <report@bugs.python.org>
wrote:

>
> Guido van Rossum added the comment:
>
> > Note: following the precedent of cmath.e and cmath.pi, cmath.nan and
> cmath.inf should have type *float*.  Let's not get into the business of
> deciding *which* complex infinities and nans to represent.
>
> Agreed.
>
> ----------
>
> _______________________________________
> Python tracker <report@bugs.python.org>
> <http://bugs.python.org/issue23229>
> _______________________________________
>
msg233951 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2015-01-13 17:17
> Note, the reason I proposed nanj (and infj) is that these are produced by
> repr() of complex numbers. Having them in the cmath module provides a place
> to document them which will then be searchable.

Another solution would be to change repr() of complex if imaginary component is not finite number to the form complex(x, y).
msg233952 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2015-01-13 17:27
> Having them in the cmath module provides a place
to document them which will then be searchable.

Okay, makes sense.

One of the reasons I'm a bit unhappy with the idea of adding infj and nanj is that it seems like an encouragement to expect "eval(repr(z))" to work (i.e., recover the original value of z), and because Python doesn't have strict imaginary numbers (i.e., numbers with no real part at all), that fails:

>>> from math import inf, nan
>>> infj = complex(0.0, inf)
>>> nanj = complex(0.0, nan)
>>> z = complex(0.0, -inf)
>>> w = eval(repr(z))
>>> z
-infj
>>> w  # real part has the "wrong" sign
(-0-infj)

But that's a pretty hollow objection, because this really has nothing to do with inf and nan; it's a problem with finite values, too:

>>> z = complex(0.0, -3.4)
>>> w = eval(repr(z))
>>> z
-3.4j
>>> w
(-0-3.4j)

So I'll add infj and nanj if the consensus is that they're useful.
msg233953 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2015-01-13 17:31
> Another solution would be to change repr() of complex if imaginary
> component is not finite number to the form complex(x, y).

That wouldn't help with the str(), though, unless you're proposing to change that, too.
msg233954 - (view) Author: Guido van Rossum (gvanrossum) * (Python committer) Date: 2015-01-13 18:08
I don't understand why w ends up having -0 as the real part. For floats, at
least, we've done a lot of work to make eval(repr()) roundtrip correctly in
all cases. Is the issue with complex superficial (we should fix eval or
repr) or deep (if we fixed it something else would be broken)?

On Tue, Jan 13, 2015 at 9:27 AM, Mark Dickinson <report@bugs.python.org>
wrote:

>
> Mark Dickinson added the comment:
>
> > Having them in the cmath module provides a place
> to document them which will then be searchable.
>
> Okay, makes sense.
>
> One of the reasons I'm a bit unhappy with the idea of adding infj and nanj
> is that it seems like an encouragement to expect "eval(repr(z))" to work
> (i.e., recover the original value of z), and because Python doesn't have
> strict imaginary numbers (i.e., numbers with no real part at all), that
> fails:
>
> >>> from math import inf, nan
> >>> infj = complex(0.0, inf)
> >>> nanj = complex(0.0, nan)
> >>> z = complex(0.0, -inf)
> >>> w = eval(repr(z))
> >>> z
> -infj
> >>> w  # real part has the "wrong" sign
> (-0-infj)
>
> But that's a pretty hollow objection, because this really has nothing to
> do with inf and nan; it's a problem with finite values, too:
>
> >>> z = complex(0.0, -3.4)
> >>> w = eval(repr(z))
> >>> z
> -3.4j
> >>> w
> (-0-3.4j)
>
> So I'll add infj and nanj if the consensus is that they're useful.
>
> ----------
>
> _______________________________________
> Python tracker <report@bugs.python.org>
> <http://bugs.python.org/issue23229>
> _______________________________________
>
msg233956 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2015-01-13 18:56
> I don't understand why w ends up having -0 as the real part.

Because "-3.4j" is interpreted as "-complex(0, 3.4)".
msg233957 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2015-01-13 19:24
> Is the issue with complex superficial

Unfortunately not: something like this is fairly inescapable.  The problem is that when you do (for example)  5 - 6j  you're in effect subtracting complex(0.0, 6.0) from complex(5.0, 0.0): you've invented a real part of 0.0 for the second term and an imaginary part of 0.0 for the first term.  And since 0.0 is *not* an identity for addition (-0.0 + 0.0 is 0.0, not -0.0, under the usual rounding modes), signs of zeros tend to get lost.

So the safe way to construct a complex number is to avoid any actual arithmetic by using complex(real_part, imag_part) rather than real_part + imag_part*1j.  I rather like Serhiy's idea of making the complex repr have this form, except that the change would probably break existing code.  (And the str should really stay in the current expected human-readable format, so the problems with infj and nanj don't go away.)

Another possible "fix" is to introduce a new 'imaginary' type, such that the type of an imaginary literal is now 'imaginary' rather than 'complex', and arithmetic operations like addition can special-case the addition of a float to an 'imaginary' instance to produce a complex number with exactly the right bits. The C standardisation folks already tried this: C99 introduces optional "imaginary" types and a new _Imaginary keyword, but last time I looked almost none of the popular compilers supported those types.  (I think Intel's icc might be an exception.)  While this works as a technical solution, IMO the cure is worse than the disease; I don't want to think about the user-confusion that would result from having separate "complex" and "imaginary" types.
msg233962 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2015-01-13 19:38
> Another possible "fix" is to introduce a new 'imaginary' type, such that the type of an imaginary literal is now 'imaginary' rather than 'complex', and arithmetic operations like addition can special-case the addition of a float to an 'imaginary' instance to produce a complex number with exactly the right bits. The C standardisation folks already tried this: C99 introduces optional "imaginary" types and a new _Imaginary keyword, but last time I looked almost none of the popular compilers supported those types.  (I think Intel's icc might be an exception.)  While this works as a technical solution, IMO the cure is worse than the disease; I don't want to think about the user-confusion that would result from having separate "complex" and "imaginary" types.

This type should exist only at compile time. Peephole optimizer should replace it with complex after folding constants.

Or may be repr() (and str()) should keep zero real part if imaginary part is negative and output period if real part is zero. For now:

>>> z = complex(0.0, 3.4); z; eval(repr(z))
3.4j
3.4j
>>> z = complex(0.0, -3.4); z; eval(repr(z))
-3.4j
(-0-3.4j)
>>> z = complex(-0.0, 3.4); z; eval(repr(z))
(-0+3.4j)
3.4j
>>> z = complex(-0.0, -3.4); z; eval(repr(z))
(-0-3.4j)
-3.4j

But all this perhaps is offtopic here.
msg233963 - (view) Author: Guido van Rossum (gvanrossum) * (Python committer) Date: 2015-01-13 19:42
OK, let's not try to resolve that issue, we can just note it in the docs.
BTW I don't want repr() of a complex number to use the complex(..., ...)
notation -- it's too verbose.

On Tue, Jan 13, 2015 at 11:38 AM, Serhiy Storchaka <report@bugs.python.org>
wrote:

>
> Serhiy Storchaka added the comment:
>
> > Another possible "fix" is to introduce a new 'imaginary' type, such that
> the type of an imaginary literal is now 'imaginary' rather than 'complex',
> and arithmetic operations like addition can special-case the addition of a
> float to an 'imaginary' instance to produce a complex number with exactly
> the right bits. The C standardisation folks already tried this: C99
> introduces optional "imaginary" types and a new _Imaginary keyword, but
> last time I looked almost none of the popular compilers supported those
> types.  (I think Intel's icc might be an exception.)  While this works as a
> technical solution, IMO the cure is worse than the disease; I don't want to
> think about the user-confusion that would result from having separate
> "complex" and "imaginary" types.
>
> This type should exist only at compile time. Peephole optimizer should
> replace it with complex after folding constants.
>
> Or may be repr() (and str()) should keep zero real part if imaginary part
> is negative and output period if real part is zero. For now:
>
> >>> z = complex(0.0, 3.4); z; eval(repr(z))
> 3.4j
> 3.4j
> >>> z = complex(0.0, -3.4); z; eval(repr(z))
> -3.4j
> (-0-3.4j)
> >>> z = complex(-0.0, 3.4); z; eval(repr(z))
> (-0+3.4j)
> 3.4j
> >>> z = complex(-0.0, -3.4); z; eval(repr(z))
> (-0-3.4j)
> -3.4j
>
> But all this perhaps is offtopic here.
>
> ----------
>
> _______________________________________
> Python tracker <report@bugs.python.org>
> <http://bugs.python.org/issue23229>
> _______________________________________
>
msg233964 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2015-01-13 19:52
> This type should exist only at compile time.

But then after doing "x = 5j", "-0.0 + 5j" and "-0.0 + x" would have different values.  Yuck!

> repr() (and str()) should keep zero real part if imaginary part is negative and output period if real part is zero

Sorry, I don't see how this helps.  What do you want the repr of (for example) "complex(-0.0, 5.0)" to be, and why?  What about the cases with 0 in the imaginary part?
msg233965 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2015-01-13 19:53
> BTW I don't want repr() of a complex number to use the complex(..., ...)
notation -- it's too verbose.

Okay, fair enough.
msg233967 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2015-01-13 20:28
> Sorry, I don't see how this helps.  What do you want the repr of (for example) "complex(-0.0, 5.0)" to be, and why?  What about the cases with 0 in the imaginary part?

Ah, it doesn't help in this case. It helps only when the imaginary part is negative.

>>> eval('(-0.0-5j)')
(-0-5j)
>>> eval('(-0-5j)')
-5j
msg273312 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2016-08-21 19:33
Here's a patch that adds inf, infj, nan and nanj.
msg273859 - (view) Author: Roundup Robot (python-dev) Date: 2016-08-29 12:57
New changeset 4b25da63d1d0 by Mark Dickinson in branch 'default':
Issue 23229: add cmath.inf, cmath.nan, cmath.infj and cmath.nanj.
https://hg.python.org/cpython/rev/4b25da63d1d0
History
Date User Action Args
2016-08-29 12:59:24mark.dickinsonsetstatus: open -> closed
2016-08-29 12:59:03mark.dickinsonsetresolution: fixed
stage: commit review -> resolved
2016-08-29 12:57:14python-devsetnosy: + python-dev
messages: + msg273859
2016-08-23 15:21:06mark.dickinsonsetstage: commit review
versions: + Python 3.6, - Python 3.5
2016-08-21 19:33:48mark.dickinsonsetfiles: + cmath_inf_nan.patch
keywords: + patch
messages: + msg273312
2015-01-13 20:52:12gvanrossumsetnosy: - gvanrossum
2015-01-13 20:28:23serhiy.storchakasetmessages: + msg233967
2015-01-13 19:53:29mark.dickinsonsetmessages: + msg233965
2015-01-13 19:52:03mark.dickinsonsetmessages: + msg233964
2015-01-13 19:42:57gvanrossumsetmessages: + msg233963
2015-01-13 19:38:00serhiy.storchakasetmessages: + msg233962
2015-01-13 19:24:20mark.dickinsonsetmessages: + msg233957
2015-01-13 18:56:47pitrousetnosy: + pitrou
messages: + msg233956
2015-01-13 18:08:37gvanrossumsetmessages: + msg233954
2015-01-13 17:31:36mark.dickinsonsetmessages: + msg233953
2015-01-13 17:27:32mark.dickinsonsetmessages: + msg233952
2015-01-13 17:17:59serhiy.storchakasetmessages: + msg233951
2015-01-13 16:53:20gvanrossumsetmessages: + msg233950
2015-01-13 16:46:12gvanrossumsetmessages: + msg233949
2015-01-13 10:21:03mark.dickinsonsetmessages: + msg233927
2015-01-13 09:59:20serhiy.storchakasetnosy: + serhiy.storchaka
messages: + msg233926
2015-01-13 09:56:11mark.dickinsonsetmessages: + msg233925
2015-01-13 09:40:37mark.dickinsonsetnosy: + gvanrossum
2015-01-13 09:39:41mark.dickinsonsetmessages: + msg233924
2015-01-13 09:39:03hayposettitle: add inf and nan to cmath module -> add inf, nan, infj, nanj to cmath module
2015-01-13 09:38:47hayposetmessages: + msg233923
2015-01-13 09:38:14mark.dickinsonsetmessages: + msg233922
2015-01-13 09:37:43hayposetnosy: + haypo
messages: + msg233921
2015-01-13 09:37:07mark.dickinsoncreate