python / cpython

The Python programming language
https://www.python.org
Other
62.85k stars 30.11k forks source link

Add math.ulp(x): unit in the last place #83491

Closed vstinner closed 4 years ago

vstinner commented 4 years ago
BPO 39310
Nosy @tim-one, @brettcannon, @rhettinger, @mdickinson, @vstinner, @stevendaprano, @miss-islington
PRs
  • python/cpython#17965
  • python/cpython#17982
  • python/cpython#17994
  • 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: ```python assignee = None closed_at = created_at = labels = ['library', '3.9'] title = 'Add math.ulp(x): unit in the last place' updated_at = user = 'https://github.com/vstinner' ``` bugs.python.org fields: ```python activity = actor = 'tim.peters' assignee = 'none' closed = True closed_date = closer = 'vstinner' components = ['Library (Lib)'] creation = creator = 'vstinner' dependencies = [] files = [] hgrepos = [] issue_num = 39310 keywords = ['patch'] message_count = 14.0 messages = ['359846', '359859', '359882', '359883', '359891', '359898', '359927', '359929', '359937', '359938', '359939', '359940', '360032', '360082'] nosy_count = 8.0 nosy_names = ['tim.peters', 'brett.cannon', 'rhettinger', 'mark.dickinson', 'vstinner', 'stutzbach', 'steven.daprano', 'miss-islington'] pr_nums = ['17965', '17982', '17994'] priority = 'normal' resolution = 'fixed' stage = 'resolved' status = 'closed' superseder = None type = None url = 'https://bugs.python.org/issue39310' versions = ['Python 3.9'] ```

    vstinner commented 4 years ago

    In bpo-39288, I added math.nextafter(x, y) function. I propose to now add math.ulp() companion function.

    Examples from tests of my PR:

    self.assertEqual(math.ulp(1.0), sys.float_info.epsilon)
    self.assertEqual(math.ulp(2.0 ** 52), 1.0)
    self.assertEqual(math.ulp(2.0 ** 53), 2.0)
    self.assertEqual(math.ulp(2.0 ** 64), 4096.0)

    Unit in the last place:

    In numpy, I found two references to ULP:

    Attached PR implements math.ulp(x).

    vstinner commented 4 years ago

    See also math.isclose() and its PEP-485 which mentions ULP (Unit in the Last Place)... in the "Inappropriate uses" section :-)

    Extract of an old python-ideas discussion on adding nextafter():

    "It's also a little weird to jump from nextafter to isclose, since AFAIK they have pretty much non-overlapping use cases..."

    https://mail.python.org/pipermail/python-ideas/2017-February/044832.html

    Other links:

    tim-one commented 4 years ago

    +1

    stevendaprano commented 4 years ago

    Thank you Victor!

    Any chance you could look at fma too? bpo-29282

    (The reward for a job well done is to be given more jobs :-)

    mdickinson commented 4 years ago

    [Steven]

    Any chance you could look at fma too? bpo-29282

    fma is hard, for reasons explained in the issue you linked to. If you have suggestions for resolving the difficulties, please do add them to that issue.

    vstinner commented 4 years ago

    New changeset 0b2ab21956fbab8eab6d064060d4544499730316 by Victor Stinner in branch 'master': bpo-39310: Add math.ulp(x) (GH-17965) https://github.com/python/cpython/commit/0b2ab21956fbab8eab6d064060d4544499730316

    vstinner commented 4 years ago

    New changeset 9362f8526e42157baf27df982b16f23f212c3c3a by Victor Stinner in branch '3.8': bpo-39310: Update sys.float_info documentation (GH-17982) https://github.com/python/cpython/commit/9362f8526e42157baf27df982b16f23f212c3c3a

    miss-islington commented 4 years ago

    New changeset dfe159ca552870f801e34cc57e9bb7d6836ce7df by Miss Islington (bot) in branch '3.7': bpo-39310: Update sys.float_info documentation (GH-17982) https://github.com/python/cpython/commit/dfe159ca552870f801e34cc57e9bb7d6836ce7df

    brettcannon commented 4 years ago

    Can I just say that "ulp" is totally non-obvious what that even means unless you have a specific math background?

    brettcannon commented 4 years ago

    And sorry if that last response from me came off as grumpy; it wasn't meant to. I just had no clue what you were adding based on the name.

    vstinner commented 4 years ago

    Can I just say that "ulp" is totally non-obvious what that even means unless you have a specific math background?

    The math.ulp() documentation explicitly says: https://docs.python.org/dev/library/math.html#math.ulp

    ULP stands for “Unit in the Last Place”.

    The term "ulp" is commonly used when talking about IEEE 754 floating point numbers. It is used in numpy and Java for example.

    test_math.py already had an ulp() function which was a pure Python implementation.

    The term is also commonly used in math articles.

    If you don't know the term "ulp", it's likely a *good thing*. You didn't have to suffer with rounding issues :-D

    stevendaprano commented 4 years ago

    I hear what you are saying, but "ulp" is the standard term of art. Sure it is non-obvious until you learn it, just like other technical terms like "mro", "abc" or "ast".

    Mathematics and numeric programming are rife with short names that are non-obvious and often ambiguous with "ordinary" words, e.g.

    sin, tan, log, nan

    to mention just a few. "ulp" is a technical, and subtle, concept to grasp, and no easier to understand when spelled out as "unit in last place".

    At least ulp is a TLA from English, unlike (say) "sine" which ultimately derives from the Sanscrit word "jya" (chord), via Arabic and Latin. If you've ever wondered if the trigonometric sin() function is related to the sinus cavities in your nose, yes it is :-)

    mdickinson commented 4 years ago

    [Brett]

    Can I just say that "ulp" is totally non-obvious what that even means unless you have a specific math background?

    It's a good point. I guess we have a choice between using the domain-specific standard-ish name (which should be immediately meaningful to experts, but doesn't give much of a hint to non-experts) or using something more descriptive (which then risks confusing experts until they figure out "oh, that's just ulp").

    There's also the option of spelling it out as "unit_in_last_place", but I'm not sure that benefits anyone.

    For meaningful descriptive names, "float_resolution" or "gap_to_next" are about the best I can come up with. "precision" is too ambiguous.

    NumPy has "numpy.spacing". But this exhibits exactly the trap of not using the "ulp" name: on a first glance, I incorrectly decided that NumPy didn't implement a ulp function. Then, having found numpy.spacing, I had to read the description carefully in order to recognise that "oh, this is just ulp". (Actually, I had to do more, since the description doesn't make all the corner cases clear, and in fact is currently wrong for powers of 2.)

    If we can get people can coalesce around a preferred alternative name, we could consider changing this.

    tim-one commented 4 years ago

    ulp() is the right name: universally understood by those who know how to use it, and easy to find exhaustive web explanations for those who don't.

    In a different context, spelling out some variant of Hypertext_Transfer_Protocol would be as wrong-headed to avoid the "cryptic" http.