Skip to content

Incorrect docstring of math.exp (math.e vs Euler's number) #101594

Description

@skirpichev
>>> import math
>>> help(math.exp)
Help on built-in function exp in module math:

exp(x, /)
    Return e raised to the power of x.

I would rephrase this as: "Return the exponential of x." (like the Mathematica docs). Same for expm1() or with log()/log1p().

The problem with the current text is that here is a disambiguation of the Euler's number e vs the math.e (an approximation of). Sometimes this is explained in the sphinx docs: e.g. for same exp() it says "Return e raised to the power x, where e = 2.718281… is the base of natural logarithms.". But sometimes not: e.g. for log() or log1p().

I think, using "the exponential of" or "the natural logarithm of" in the docstrings and sphinx docs will be better.

Linked PRs

Activity

  1. stevendaprano commented on Feb 6, 2023

    @stevendaprano
    Member

    "Return the exponential of x" implies that it returns x to some power, which is wrong. Even if we read it the other way, "the exponential of x" doesn't tell us what number is being raised to the power of x. Which exponential? There are an infinite number of possible exponentials (something) to the power of x.

    The current description is much better than the description in the Mathematica docs, where you have to click on the "Details" section to finally discover that Exp[z] is converted to E^z.

    The problem with the current text is that here is a disambiguation of the Euler's number e vs the math.e (an approximation of).

    That's not a problem. We know from the relevant domain knowledge that all float constants and operations refer to floats, not real numbers. We don't need to repeat that for every single function. Unlike Mathematica, the math module does not do symbolic exact maths.

    In any case, the docstring does not have to repeat the full documentation. It is okay for the docstring to be an abbreviated summary.

  2. skirpichev commented on Feb 6, 2023

    @skirpichev
    MemberAuthor

    "Return the exponential of x" implies that it returns x to some power, which is wrong.

    Not. It's exactly e**x, see also the matlab docs, but indeed: maybe this convention is less popular. Other variants (more verbose): "the natural exponential function" or just "the exponential function", see the mathworld.

    We know from the relevant domain knowledge that all float constants and operations refer to floats, not real numbers.

    Then this will be incorrect, because the math.exp(x) is not actually the math.e**x. The mentioned full documention does explain why.

    In any case, the docstring does not have to repeat the full documentation.

    That's true. But even you above mistakenly interpreted e in the docstring as the float number math.e, that shows that there is an issue.

  3. picnixz commented on Feb 7, 2023

    @picnixz
    Member

    Here's my 2 cents: since the math module is based on the C standard and AFAIK math.exp simply calls exp from <math.h> (which itself is technically implementation-dependent), I would simply copy the description from §7.12.6.1/2, ISO/IEC 9899:1999:TC3, namely

    Compute the base-e exponential of x.

    Now, even with the above sentence or even if you say "the exponential of x", you could argue that it's still not the case since the actual result is an approximation. In particular, you cannot say that it uses Euler's number. Therefore, I don't think we need to be extremely verbose in the docstring if we have an online doc and if the actual result also depends on the libc implementation. A more precise docstring would be

    Compute an approximation of the base-e exponential of x. The result is more precise than math.e ** x.

    I however think that this is more confusing for normal users.

  4. arhadthedev commented on Feb 7, 2023

    @arhadthedev
    Member

    I however think that this is more confusing for normal users.

    As a normal user, I've thought that both ways are the same and exp() is just a pre-** legacy artifact. So the precision note is crucial.

  5. picnixz commented on Feb 7, 2023

    @picnixz
    Member

    What I thought users could find confusing is the term "approximation" because the way it is approximated is entirely implementation-dependent. But if it's not confusing, then it's good, I think ? I personally come from C/C++, so I've always used exp() since I was told not to use constants directly.

  6. skirpichev commented on Feb 7, 2023

    @skirpichev
    MemberAuthor

    I don't think we need to be extremely verbose in the docstring

    In fact, what's I'm suggesting is to be less verbose in docstring and sphinx docs. E.g. the docstring of exp could be:

    Return the exponential function at x.
    

    The sphinx docs:

    Return the exponential function at x. This is usually more accurate than math.e**x.
    

    And so on: we could drop remarks like (base e) or Here e is the base of natural logarithms.

  7. added a commit that references this issue on Feb 19, 2023
  8. mdickinson commented on Feb 19, 2023

    @mdickinson
    Member

    Thanks all for the discussion and suggestions, and thank you to @skirpichev for the PR.

    I'm going to close here: the maxim "Practicality beats purity" applies, and I don't think there's any practical problem being solved at this point; in particular, I think removing the references to e on the basis that they're technically ambiguous harms more than it helps.

    I agree that there's ambiguity over whether e in any given docstring means math.e or whether it refers to Euler's number, but I'm not seeing any practical problem arising from that ambiguity. The sole exception is that it should be emphasized that users should prefer exp(x) over math.e**x for accuracy reasons, and the docs already make that point.

    Technically the same applies to log: log(x) should be preferred over log(x, math.e), but in practice it makes no difference - assuming IEEE 754 arithmetic, log(math.e) evaluates to 1.0 under correct rounding, and on most platforms the libm accuracy is good enough that log(math.e) does in fact give exactly 1.0. So log(x) and log(x, math.e) give exactly the same result; the latter is just slightly less efficient. So this doesn't seem worth calling out in the docs.

  9. skirpichev commented on Feb 19, 2023

    @skirpichev
    MemberAuthor

    @mdickinson, thank you for review.

    So log(x) and log(x, math.e) give exactly the same result;

    If we can guarantee that, sorry for a bit off-topic, then base=math.e sounds to be a good default value. What if we adapt the inspect module to keep "symbolic" name in the Signature, does this make sense for you?

  10. mdickinson commented on Feb 19, 2023

    @mdickinson
    Member

    @skirpichev Yes, that seems reasonable, if it can be achieved. It's philosophically wrong in that it doesn't match what the implementation actually does, and that still bugs me a little, but I think it passes the "practicality beats purity" test.

  11. skirpichev commented on Feb 19, 2023

    @skirpichev
    MemberAuthor

    that seems reasonable, if it can be achieved.

    Yes, I've a toy implementation: #89381 (comment)

    It's philosophically wrong in that it doesn't match what the implementation actually does

    Instead of "calculated as log(x)/log(base)" we could state something like "equal to log(x)/log(base)".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    docsDocumentation in the Doc dir

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions