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

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

Closed
skirpichev opened this issue Feb 6, 2023 · 10 comments
Closed

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

skirpichev opened this issue Feb 6, 2023 · 10 comments
Labels
docs Documentation in the Doc dir

Comments

@skirpichev
Copy link
Member

skirpichev commented Feb 6, 2023

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

@stevendaprano
Copy link
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.

@skirpichev
Copy link
Member Author

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

@picnixz
Copy link
Member

picnixz commented Feb 7, 2023

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.

@arhadthedev
Copy link
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.

@picnixz
Copy link
Member

picnixz commented Feb 7, 2023

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.

@skirpichev
Copy link
Member Author

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.

@mdickinson
Copy link
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.

@mdickinson mdickinson closed this as not planned Won't fix, can't repro, duplicate, stale Feb 19, 2023
@skirpichev
Copy link
Member Author

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

@mdickinson
Copy link
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.

@skirpichev
Copy link
Member Author

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
Labels
docs Documentation in the Doc dir
Projects
None yet
Development

No branches or pull requests

5 participants