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 and nan to math module #67374

Closed
ethanfurman opened this issue Jan 7, 2015 · 26 comments
Closed

add inf and nan to math module #67374

ethanfurman opened this issue Jan 7, 2015 · 26 comments
Assignees
Labels
easy stdlib Python modules in the Lib dir type-feature A feature request or enhancement

Comments

@ethanfurman
Copy link
Member

BPO 23185
Nosy @malemburg, @gvanrossum, @rhettinger, @mdickinson, @pitrou, @vstinner, @ericvsmith, @ethanfurman, @serhiy-storchaka
Files
  • math_inf_nan.patch
  • math_inf_nan2.patch
  • math_inf_nan3.patch
  • math_inf_nan4.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 2015-01-11.11:56:31.326>
    created_at = <Date 2015-01-07.16:33:44.317>
    labels = ['easy', 'type-feature', 'library']
    title = 'add inf and nan to math module'
    updated_at = <Date 2015-01-13.09:37:33.819>
    user = 'https://github.com/ethanfurman'

    bugs.python.org fields:

    activity = <Date 2015-01-13.09:37:33.819>
    actor = 'mark.dickinson'
    assignee = 'mark.dickinson'
    closed = True
    closed_date = <Date 2015-01-11.11:56:31.326>
    closer = 'mark.dickinson'
    components = ['Library (Lib)']
    creation = <Date 2015-01-07.16:33:44.317>
    creator = 'ethan.furman'
    dependencies = []
    files = ['37633', '37634', '37635', '37647']
    hgrepos = []
    issue_num = 23185
    keywords = ['patch', 'easy']
    message_count = 26.0
    messages = ['233580', '233581', '233582', '233583', '233585', '233588', '233591', '233593', '233594', '233595', '233596', '233598', '233601', '233602', '233607', '233630', '233674', '233692', '233720', '233737', '233842', '233843', '233894', '233912', '233917', '233920']
    nosy_count = 11.0
    nosy_names = ['lemburg', 'gvanrossum', 'rhettinger', 'mark.dickinson', 'pitrou', 'vstinner', 'eric.smith', 'stutzbach', 'ethan.furman', 'python-dev', 'serhiy.storchaka']
    pr_nums = []
    priority = 'normal'
    resolution = 'fixed'
    stage = 'resolved'
    status = 'closed'
    superseder = None
    type = 'enhancement'
    url = 'https://bugs.python.org/issue23185'
    versions = ['Python 3.5']

    @ethanfurman
    Copy link
    Member Author

    Proposal:

      math.nan = float('nan')
      math.inf = float('inf')

    Guido's approval:

    https://mail.python.org/pipermail/python-ideas/2015-January/030775.html

    Followup question:

    Do we add a math.neginf, or somesuch, for float('-inf')? Or just use -math.inf?

    @ethanfurman ethanfurman added easy type-feature A feature request or enhancement labels Jan 7, 2015
    @vstinner
    Copy link
    Member

    vstinner commented Jan 7, 2015

    Do we add a math.neginf, or somesuch, for float('-inf')? Or just use -math.inf?

    I would prefer to only add inf.

    It looks like float("-inf") and -float("inf") have exactly the same IEEE 754 representation (at least on my x86_64 CPU):

    >>> struct.pack("d", float("-inf")) == struct.pack("d", -float("inf"))
    True

    @berkerpeksag berkerpeksag added the stdlib Python modules in the Lib dir label Jan 7, 2015
    @mdickinson
    Copy link
    Member

    Sounds good to me.

    Do we add a math.neginf

    IMO no: -inf should be fine.

    @mdickinson
    Copy link
    Member

    float("-inf") and -float("inf") have exactly the same IEEE 754 representation

    Indeed: there's only one negative infinity (and only one positive infinity) in IEEE 754 binary64 format.

    @mdickinson
    Copy link
    Member

    Implementation suggestion: if possible, use the _Py_dg_stdnan and _Py_dg_infinity functions from Python/dtoa.c. These are a little safer than the Py_NAN and Py_HUGE_VAL macros, and will give results consistent with the float("inf") and float("nan") constructions.

    @vstinner
    Copy link
    Member

    vstinner commented Jan 7, 2015

    Oh, NaN can be signed?

    >>> struct.pack("d", float("nan"))
    b'\x00\x00\x00\x00\x00\x00\xf8\x7f'
    >>> struct.pack("d", float("-nan"))
    b'\x00\x00\x00\x00\x00\x00\xf8\xff'
    >>> struct.pack("d", -float("nan"))
    b'\x00\x00\x00\x00\x00\x00\xf8\xff'
    >>> struct.pack("d", -float("-nan"))
    b'\x00\x00\x00\x00\x00\x00\xf8\x7f'

    Why does Python return the same representation for positive and negative NaN?

    >>> float("nan")
    nan
    >>> float("-nan")
    nan

    @mdickinson
    Copy link
    Member

    Why does Python return the same representation for positive and negative NaN?

    History, perhaps? In any case, the sign of a NaN isn't useful information in the same way that the sign of an infinity is. The IEEE 754 standard explicitly refuses to attach any meaning to the sign bit of a NaN. And if we were aiming for a full and faithful representation of NaNs, we'd want to output the payload, too (which is just about as meaningless / meaningful as the sign bit).

    @serhiy-storchaka
    Copy link
    Member

    There are several different NaNs.

    >>> x = struct.unpack('d', b'\x00\x00\x00\x00\x00\x00\xf8\x7f')[0]
    >>> x
    nan
    >>> x == x
    False
    >>> struct.pack('d', x)
    b'\x00\x00\x00\x00\x00\x00\xf8\x7f'
    >>> x = struct.unpack('d', b'\x00\x00\x00\x00\x00\x00\xf9\x7f')[0]
    >>> x
    nan
    >>> x == x
    False
    >>> struct.pack('d', x)
    b'\x00\x00\x00\x00\x00\x00\xf9\x7f'

    Interesting, but 0*inf and inf-inf return values with the same representation as float('-nan'), not float('nan').

    >>> inf = float("inf")
    >>> struct.pack('d', 0*inf)
    b'\x00\x00\x00\x00\x00\x00\xf8\xff'
    >>> struct.pack('d', inf-inf)
    b'\x00\x00\x00\x00\x00\x00\xf8\xff'
    >>> struct.pack('d', float('nan'))
    b'\x00\x00\x00\x00\x00\x00\xf8\x7f'
    >>> struct.pack('d', float('-nan'))
    b'\x00\x00\x00\x00\x00\x00\xf8\xff'

    @pitrou
    Copy link
    Member

    pitrou commented Jan 7, 2015

    By tweaking the grammar we can have math.-inf.

    @mdickinson
    Copy link
    Member

    but 0*inf and inf-inf return values with the same representation as float('-nan'), not float('nan')

    Right: that's because Intel's "default" NaN (i.e., the float it produces as a result of any invalid operation) has its sign bit set.

    @mdickinson
    Copy link
    Member

    By tweaking the grammar we can have math.-inf.

    AAAARRGH!

    @mdickinson
    Copy link
    Member

    Here's a patch.

    @mdickinson mdickinson self-assigned this Jan 7, 2015
    @mdickinson
    Copy link
    Member

    Thanks for the review comments. Here's an updated patch taking the review comments into account (and fixing the spelling of PY_NAN, which should have been Py_NAN).

    @mdickinson
    Copy link
    Member

    One more patch, fixing a misplaced period.

    @vstinner
    Copy link
    Member

    vstinner commented Jan 7, 2015

    "History, perhaps? In any case, the sign of a NaN isn't useful information in the same way that the sign of an infinity is. The IEEE 754 standard explicitly refuses to attach any meaning to the sign bit of a NaN. And if we were aiming for a full and faithful representation of NaNs, we'd want to output the payload, too (which is just about as meaningless / meaningful as the sign bit)."

    So I understand that adding a math.neg_nan would be useless. As adding one constant per possible "NaN" value :-) If I recall correctly the IEEE 754 standard, there is not single NaN value, but a range of NaN.

    "Two kinds of NaN: a quiet NaN (qNaN) and a signaling NaN (sNaN). A NaN may carry a payload that is intended for diagnostic information indicating the source of the NaN. The sign of a NaN has no meaning, but it may be predictable in some circumstances." says Wikipedia.

    Well, the current definition of math.nan makes sense, it's the same value than float("nan").

    Note: On python-ideas, I asked if math.nan and math.inf should be singleton (as it was requested for float("0.0") in issue bpo-4024). The answer is no.

    @mdickinson
    Copy link
    Member

    I have an updated patch taking into account the most recent review comments (for which thanks!), but it's at home; I'll upload it this evening (UTC+00:00).

    @mdickinson
    Copy link
    Member

    New patch, addressing review comments.

    @vstinner
    Copy link
    Member

    vstinner commented Jan 8, 2015

    Except of my small suggestion on the doc (see the review), math_inf_nan4.patch looks good to me.

    @serhiy-storchaka
    Copy link
    Member

    May be make math.inf and math.nan special objects so that for all x (except inf and nan):

    x < math.inf
    x > -math.inf
    not (x < math.nan)
    not (x > math.nan)

    @vstinner
    Copy link
    Member

    vstinner commented Jan 9, 2015

    2015-01-09 8:16 GMT+01:00 Serhiy Storchaka <report@bugs.python.org>:

    May be make math.inf and math.nan special objects so that for all x (except inf and nan):

    What do you mean? Implement a subtype of float and override some methods?

    x < math.inf
    x > -math.inf

    It's already the case for int, float and decimal.Decimal.

    not (x < math.nan)
    not (x > math.nan)

    Comparison to nan always return False.

    I would be better to raise an error when nan is compared to other numbers (I mean operations like a>b, not a==b), but Python was not designed like that (nor the IEEE 754?).

    >>> sorted((nan, 1, nan, 2))
    [nan, 1, nan, 2]

    Sorting with NaN is a common issue :-/ See for example:
    https://stackoverflow.com/questions/4240050/python-sort-function-breaks-in-the-presence-of-nan

    Anyway, changing NaN behaviour is out of the scope of this issue!

    @python-dev
    Copy link
    Mannequin

    python-dev mannequin commented Jan 11, 2015

    New changeset cf4bf577749c by Mark Dickinson in branch 'default':
    Issue bpo-23185: add math.inf and math.nan constants.
    https://hg.python.org/cpython/rev/cf4bf577749c

    @mdickinson
    Copy link
    Member

    Committed. Thanks for all the helpful review comments!

    @gvanrossum
    Copy link
    Member

    Should inf and nan be added to cmath too? It has e and pi and isnan() and isinf()...

    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.

    @vstinner
    Copy link
    Member

    Guido van Rossum added the comment:

    Should inf and nan be added to cmath too? It has e and pi and isnan() and isinf()...

    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.

    Since it's a different module and we are talking about more and
    different constants, I suggest to open a new issue.

    @mdickinson
    Copy link
    Member

    Should inf and nan be added to cmath too?

    Hmm; probably, yes. I'll open an issue.

    so the point could be made that there should be a constant named complex.nanj

    Yes, I suppose it could (along with infj, of course). I don't like it much, and I suspect it would get almost no uses. complex(0, inf) and complex(0, nan) seem like good enough spellings.

    @mdickinson
    Copy link
    Member

    Opened issue bpo-23229.

    @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
    easy stdlib Python modules in the Lib dir type-feature A feature request or enhancement
    Projects
    None yet
    Development

    No branches or pull requests

    7 participants