classification
Title: math.exp documentation is misleading
Type: Stage: resolved
Components: Documentation Versions: Python 3.7
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: docs@python Nosy List: belopolsky, docs@python, mark.dickinson, rhettinger, serhiy.storchaka, stutzbach, terry.reedy, tim.peters
Priority: normal Keywords:

Created on 2017-03-31 20:08 by belopolsky, last changed 2017-05-08 17:14 by belopolsky. This issue is now closed.

Pull Requests
URL Status Linked Edit
PR 951 merged mark.dickinson, 2017-04-01 14:51
PR 1073 merged serhiy.storchaka, 2017-04-10 05:34
Messages (21)
msg290937 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2017-03-31 20:08
The math.exp(x) function is documented to "Return e**x" <https://docs.python.org/3/library/math.html#math.exp>.  This is misleading because even in the simplest case, math.exp(x) is not the same as math.e ** x:

>>> import math
>>> math.exp(2) - math.e ** 2
8.881784197001252e-16

I suggest using e<sup>x instead of e**x to distinguish between Python syntax and mathematical operation and change "Return e**x" to "Return e<sup>x, the base-e exponential of x."
msg290939 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2017-03-31 20:29
This is because math.e is not the same as e.
msg290940 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2017-03-31 20:36
> This is because math.e is not the same as e.

Right.  That's why I think it would be nice to distinguish math.e and the base of the natural logarithm typographically in the docs.  Can we use sphinx math mode?  If not, I would use italic for the mathematical e.
msg290941 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2017-03-31 20:54
*e*:sup:`x` ? I like this idea.
msg290948 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2017-03-31 23:11
I suggest changing the main docs to match the existing docstring, "Return e raised to the power of x."  

The exp() function is a thin wrapper around the C math library and where it is documented as "compute e (the base of natural logarithms) raised to the power x" or "e raised to the power X (where e is the base of the natural system of logarithms, approximately 2.71828)."  Our docs shouldn't make more or fewer promises than the upstream libraries are making.

Perhaps there can be a general note about reading too much into the math module implementation details.  We expect some relationships to only be approximate: log(x)+1≈log1p(x), log2(x)≈log(x,2.0), exp(lgamma(x))≈gamma(x), sqrt(x)≈x**0.5, etc.  These are floating point math library "facts of life".

* http://www.slac.stanford.edu/comp/unix/package/rtems/doc/html/libm/libm.info.exp.html

* https://www.gnu.org/software/libc/manual/html_node/Exponents-and-Logarithms.html
msg290978 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2017-04-01 10:50
> I suggest changing the main docs to match the existing docstring, "Return e raised to the power of x."  

+1 for this description.
msg290986 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2017-04-01 14:53
PR made. New wording is:

"""
Return e raised to the power *x*, where e = 2.718281... is the base of natural logarithms.
"""
msg291285 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2017-04-07 18:55
Is math.exp(x) always more accurate than math.e ** x?  If so, doc could say so.  Otherwise, should this be closed?
msg291322 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2017-04-08 06:43
Not always. For example for x = 0 both methods give the same exact result.
msg291381 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2017-04-09 16:12
> Is math.exp(x) always more accurate than math.e ** x?

As Serhiy says: not always, and in general the answer is going to depend on the relative quality of the libm implementations of pow and exp. But on typical machines, it is going to be true that `math.exp(x)` is a better (faster, more accurate) way of computing the exponential function than `math.e ** x`. (Similarly, `math.sqrt(x)` should be preferred over `x ** 0.5`.) I'm not sure whether it's worth encoding such recommendations in the documentation or not.
msg291387 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2017-04-09 18:55
> Is math.exp(x) always more accurate than math.e ** x? 

It is usually at least as accurate, but we can't really guarantee anything because math.exp does whatever the underlying C math library does (so good libary -> good result, bad library -> bad result).

Rather than gum-up the math library docs, I suggest having a FAQ entry or wiki entry somewhere.  Getting extreme accuracy is a nebulous topic in general and even more so in Python (where there is very little you can do to prevent double rounding and whatnot).  

In addition to extreme accuracy issues, there are also performance issues which will vary from implementation to implementation and from release to release.

Historically, the docs have tried to rise above the fray and make very few if any promises about accuracy or speed.  This should be doubly true when it comes to numerical methods which are a mix of art, science, and dark art (and where the answers to "what is best" may change depending on the range of input values).
msg291389 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2017-04-09 19:11
To include corner cases, I should have asked 'at least as accurate' rather than 'more accurate'.  It would be a sad libm that had specialized functions worse than pow, since the specialized functions could, at worse, use pow.

For an expert point of view, the reason for math to have the specialized functions is to give access to functions in the libm of the compiler used. A beginner ignorant of such things might wonder whether exp and sqrt are just trivial abbreviations, and if not, which to use.  I believe this question has appeared on python-list.  It definitely has on StackOverflow.

For e**x, there is, for instance,
https://stackoverflow.com/questions/30756983/difference-between-math-exp2-and-math-e2
with this comment "Voting to reopen. There's more going on here than simply "floating-point is inaccurate". In particular, as the two answers explain, there are good reasons to expect exp(x) to be more accurate than e**x. – Mark Dickinson " ;-).

Searching "[python] math.sqrt pow" gets more hits.
https://stackoverflow.com/questions/18965524/exponentiation-in-python-should-i-prefer-operator-instead-of-math-pow-and-m
https://stackoverflow.com/questions/33684948/difference-between-1-2-math-sqrt-and-cmath-sqrt
and multiple questions about relative speed.

So I am inclined to add "This is generally better than math.e ** x and math.pow(e, 0.5)." (for math.exp) and "than x ** 0.5 and math.pow(x, 0.5)" for math.sqrt, and similarly for cmath.sqrt).
msg291390 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2017-04-09 19:17
Raymond added his comment while I was writing mine.  A FAQ with added caveats might be even better, but it will be mostly missed.  If we add one, I might add a comment to some of the SO questions.
msg291391 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2017-04-09 19:27
FWIW, these kind of nuances really aren't beginner topics.
msg291393 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2017-04-09 19:40
Nuances of expm1(), log1p(), log2() and log10() aren't beginner topics, but they are documented. I think it wouldn't harm if add "This is usually more accurate than ``e ** x`` or ``pow(e, x)``."

The only issue is how to distinguish ``math`` constant ``e`` from mathematical constant *e*.
msg291401 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2017-04-10 00:53
> The only issue is how to distinguish ``math`` constant ``e``
> from mathematical constant *e*.

Sorry, I think you're inventing an issue here.  ``math.e`` is the nearest representable value to the mathematical constant *e*.  This is no more interesting or useful that distinguishing ``math.pi`` from the mathematical constant *pi*.  I don't know of any other language that tries to split hairs like this.
msg291410 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2017-04-10 04:49
This is the original issue, it isn't invented by me. ``math.e`` is the nearest
representable value to the mathematical constant *e* and ``math.exp(x)`` is 
the nearest representable value to the mathematical constant *e* raised to the 
power *x*, but not the nearest representable value to ``math.e`` raised to the 
power *x*.
msg291412 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2017-04-10 05:36
Proposed patch applies Mark's fix to math.expm1() and cmath.exp(), adds the accuracy note to math.exp(), adds italic to mathematical constants, fixes empty lines.
msg292963 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2017-05-04 05:43
Could anybody please make a review of PR 1073?
msg292971 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2017-05-04 09:25
New changeset dbaf746b6de0ee431c809d3175ab40ccc18898a8 by Serhiy Storchaka in branch 'master':
bpo-29956: Improve the math.exp() related documentation. (#1073)
https://github.com/python/cpython/commit/dbaf746b6de0ee431c809d3175ab40ccc18898a8
msg293180 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2017-05-07 07:18
Can this be closed?
History
Date User Action Args
2017-05-08 17:14:18belopolskysetstatus: open -> closed
resolution: fixed
stage: resolved
2017-05-07 07:18:17mark.dickinsonsetmessages: + msg293180
2017-05-04 09:25:12serhiy.storchakasetmessages: + msg292971
2017-05-04 05:43:00serhiy.storchakasetmessages: + msg292963
2017-04-10 05:36:57serhiy.storchakasetmessages: + msg291412
2017-04-10 05:34:38serhiy.storchakasetpull_requests: + pull_request1216
2017-04-10 04:49:12serhiy.storchakasetmessages: + msg291410
2017-04-10 00:53:04rhettingersetmessages: + msg291401
2017-04-09 19:40:06serhiy.storchakasetmessages: + msg291393
2017-04-09 19:27:12rhettingersetmessages: + msg291391
2017-04-09 19:17:17terry.reedysetmessages: + msg291390
2017-04-09 19:11:49terry.reedysetmessages: + msg291389
2017-04-09 18:55:02rhettingersetmessages: + msg291387
2017-04-09 16:12:54mark.dickinsonsetmessages: + msg291381
2017-04-08 06:43:07serhiy.storchakasetmessages: + msg291322
2017-04-07 18:55:09terry.reedysetnosy: + terry.reedy
messages: + msg291285
2017-04-01 14:53:01mark.dickinsonsetmessages: + msg290986
2017-04-01 14:51:42mark.dickinsonsetpull_requests: + pull_request1133
2017-04-01 10:50:49mark.dickinsonsetmessages: + msg290978
2017-03-31 23:11:58rhettingersetmessages: + msg290948
2017-03-31 20:54:13serhiy.storchakasetmessages: + msg290941
2017-03-31 20:36:18belopolskysetmessages: + msg290940
2017-03-31 20:29:30serhiy.storchakasetnosy: + rhettinger, stutzbach, tim.peters, mark.dickinson, serhiy.storchaka
messages: + msg290939
2017-03-31 20:08:37belopolskycreate