Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add inf, nan, infj, nanj to cmath module #67418

Closed
mdickinson opened this issue Jan 13, 2015 · 23 comments
Closed

add inf, nan, infj, nanj to cmath module #67418

mdickinson opened this issue Jan 13, 2015 · 23 comments
Assignees
Labels
extension-modules C modules in the Modules dir type-feature A feature request or enhancement

Comments

@mdickinson
Copy link
Member

BPO 23229
Nosy @mdickinson, @pitrou, @vstinner, @serhiy-storchaka
Files
  • cmath_inf_nan.patch
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = 'https://github.com/mdickinson'
    closed_at = <Date 2016-08-29.12:59:24.714>
    created_at = <Date 2015-01-13.09:37:07.756>
    labels = ['extension-modules', 'type-feature']
    title = 'add inf, nan, infj, nanj to cmath module'
    updated_at = <Date 2016-08-29.12:59:24.713>
    user = 'https://github.com/mdickinson'

    bugs.python.org fields:

    activity = <Date 2016-08-29.12:59:24.713>
    actor = 'mark.dickinson'
    assignee = 'mark.dickinson'
    closed = True
    closed_date = <Date 2016-08-29.12:59:24.714>
    closer = 'mark.dickinson'
    components = ['Extension Modules']
    creation = <Date 2015-01-13.09:37:07.756>
    creator = 'mark.dickinson'
    dependencies = []
    files = ['44181']
    hgrepos = []
    issue_num = 23229
    keywords = ['patch']
    message_count = 23.0
    messages = ['233919', '233921', '233922', '233923', '233924', '233925', '233926', '233927', '233949', '233950', '233951', '233952', '233953', '233954', '233956', '233957', '233962', '233963', '233964', '233965', '233967', '273312', '273859']
    nosy_count = 5.0
    nosy_names = ['mark.dickinson', 'pitrou', 'vstinner', 'python-dev', 'serhiy.storchaka']
    pr_nums = []
    priority = 'normal'
    resolution = 'fixed'
    stage = 'resolved'
    status = 'closed'
    superseder = None
    type = 'enhancement'
    url = 'https://bugs.python.org/issue23229'
    versions = ['Python 3.6']

    @mdickinson
    Copy link
    Member Author

    As pointed out by Guido in bpo-23185, the constants inf and nan should be added to the cmath module, too.

    @mdickinson mdickinson self-assigned this Jan 13, 2015
    @mdickinson mdickinson added extension-modules C modules in the Modules dir type-feature A feature request or enhancement labels Jan 13, 2015
    @vstinner
    Copy link
    Member

    What about cmath.nanj?

    @mdickinson
    Copy link
    Member Author

    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".

    @vstinner
    Copy link
    Member

    Oh, there is also infj:

    >>> complex(0, float("inf"))
    infj
    >>> complex("infj")
    infj
    >>> complex(0, float("nan"))
    nanj
    >>> complex("nanj")
    nanj

    @vstinner vstinner changed the title add inf and nan to cmath module add inf, nan, infj, nanj to cmath module Jan 13, 2015
    @mdickinson
    Copy link
    Member Author

    @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.

    @mdickinson
    Copy link
    Member Author

    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.

    @serhiy-storchaka
    Copy link
    Member

    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?

    @mdickinson
    Copy link
    Member Author

    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.

    @gvanrossum
    Copy link
    Member

    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.

    @gvanrossum
    Copy link
    Member

    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\>


    @serhiy-storchaka
    Copy link
    Member

    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).

    @mdickinson
    Copy link
    Member Author

    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.

    @mdickinson
    Copy link
    Member Author

    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.

    @gvanrossum
    Copy link
    Member

    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\>


    @pitrou
    Copy link
    Member

    pitrou commented Jan 13, 2015

    I don't understand why w ends up having -0 as the real part.

    Because "-3.4j" is interpreted as "-complex(0, 3.4)".

    @mdickinson
    Copy link
    Member Author

    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.

    @serhiy-storchaka
    Copy link
    Member

    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.

    @gvanrossum
    Copy link
    Member

    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\>


    @mdickinson
    Copy link
    Member Author

    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?

    @mdickinson
    Copy link
    Member Author

    BTW I don't want repr() of a complex number to use the complex(..., ...)
    notation -- it's too verbose.

    Okay, fair enough.

    @serhiy-storchaka
    Copy link
    Member

    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

    @mdickinson
    Copy link
    Member Author

    Here's a patch that adds inf, infj, nan and nanj.

    @python-dev
    Copy link
    Mannequin

    python-dev mannequin commented Aug 29, 2016

    New changeset 4b25da63d1d0 by Mark Dickinson in branch 'default':
    bpo-23229: add cmath.inf, cmath.nan, cmath.infj and cmath.nanj.
    https://hg.python.org/cpython/rev/4b25da63d1d0

    @ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Labels
    extension-modules C modules in the Modules dir type-feature A feature request or enhancement
    Projects
    None yet
    Development

    No branches or pull requests

    5 participants