This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: Special case log(x, 2), log(x, 10) and pow(2, x)
Type: enhancement Stage: resolved
Components: Extension Modules Versions: Python 3.7
process
Status: closed Resolution: rejected
Dependencies: Superseder:
Assigned To: Nosy List: mark.dickinson, rhettinger, serhiy.storchaka, stutzbach
Priority: normal Keywords:

Created on 2017-11-08 12:59 by serhiy.storchaka, last changed 2022-04-11 14:58 by admin. This issue is now closed.

Messages (10)
msg305825 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2017-11-08 12:59
C99 provides functions log2(), log10() and exp2(). Is it worth to special case math.log(), math.pow() and built-in pow() for using these functions? log2(x) can be more accurate than log(x)/log(2).

There are math.log2() and math.log10(), but not math.exp2().
msg305852 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2017-11-08 14:45
There is also a GNU extension pow10().
msg305890 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2017-11-08 19:17
I'd say not. If people want `log` base 2 or 10, they should use the appropriate functions.

One potential numeric issue here is maintaining monotonicity. If we swap out libm `log` calls for `log2` calls for some inputs, we not only make the code more complicated, but also risk breaking monotonicity.

So -1 from me on the special casing.

I'd be very happy with exposing exp2 in the math library, but that's a separate issue.
msg305894 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2017-11-08 19:48
There is no two-argument libm `log`. Two-argument log(x, base) is implemented as log(x) / log(base). log(base) adds an error.

>>> import math
>>> math.log(2**31, 2)
31.000000000000004

Since two-argument log() doesn't correspond a C function, I think we are free to use more precise implementation.

We could correct also two-argument logarithms with other bases. For example:

def prec_log(x, y):
    a = math.log(x, y)
    return a + math.log(x / math.pow(y, a), y)

>>> math.log(3**20, 3)
19.999999999999996
>>> prec_log(3**20, 3)
20.0
msg305895 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2017-11-08 19:55
> There is no two-argument libm `log`. Two-argument log(x, base) is implemented as log(x) / log(base).

Yep. But log(x) / log(base) is monotonic in x for a fixed base, assuming that the libm log is. My objections still hold. :-) Please let's keep the math.log implementation simple (or at least, no more complicated than it already is).
msg305898 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2017-11-08 20:12
Also, prec_log would need significant work to make it handle all the corner cases sensibly. For example:

Python 3.6.3 (default, Oct  5 2017, 23:34:28) 
[GCC 4.2.1 Compatible Apple LLVM 8.1.0 (clang-802.0.42)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import math
>>> def prec_log(x, y):
...     a = math.log(x, y)
...     return a + math.log(x / math.pow(y, a), y)
... 
>>> math.log(3**1000, 3)
999.9999999999999
>>> prec_log(3**1000, 3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in prec_log
OverflowError: math range error
msg305899 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2017-11-08 20:17
Okay. I think that log(x, base) still will be monotonic, because log(x, nextafter(2, +INF)) <= log2(x) <= log(x, nextafter(2, -INF)).

I know about this limitation of prec_log(). It was just an example. Maybe  Tim know more robust and efficient algorithms.

Exposing exp2 in the math library would be less interesting, because we don't add errors in pow() and just call libm pow(). The latter can call exp2() internally if it provides more accuracy than exp(log(x)*y).
msg305902 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2017-11-08 20:38
-1 from me as well.  Various root finding and minimization algorithms rely (perhaps) incorrect on the functions transitioning smoothly across the inputs.

Inside the code for math.log10() and math.log2(), it would be reasonable to use the C99 functions rather than having us compute them indirectly.  But the math.log(x, b) function should be left alone IMO.
msg305907 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2017-11-08 21:20
> Exposing exp2 in the math library would be less interesting

I disagree: a libm exp2 is likely to be significantly accurate than pow(2.0, x). The latter is unlikely to be special-cased by the libm pow.

> The latter can call exp2() internally if it provides more accuracy than exp(log(x)*y).

It would be a very poor pow implementation that used exp(log(x)*y).
msg305908 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2017-11-08 21:21
> significantly accurate

significantly *more* accurate
History
Date User Action Args
2022-04-11 14:58:54adminsetgithub: 76161
2017-11-08 21:21:26mark.dickinsonsetmessages: + msg305908
2017-11-08 21:20:49mark.dickinsonsetmessages: + msg305907
2017-11-08 20:38:27rhettingersetmessages: + msg305902
2017-11-08 20:17:57serhiy.storchakasetstatus: open -> closed
resolution: rejected
messages: + msg305899

stage: resolved
2017-11-08 20:12:18mark.dickinsonsetmessages: + msg305898
2017-11-08 19:55:40mark.dickinsonsetmessages: + msg305895
2017-11-08 19:48:52serhiy.storchakasetmessages: + msg305894
2017-11-08 19:17:44mark.dickinsonsetmessages: + msg305890
2017-11-08 14:45:36serhiy.storchakasetmessages: + msg305852
2017-11-08 12:59:05serhiy.storchakacreate