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: document that math.pow is inappropriate for integers
Type: enhancement Stage: resolved
Components: Documentation Versions: Python 3.2, Python 3.3, Python 3.4, Python 2.7
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: mark.dickinson Nosy List: Ramchandra Apte, amaury.forgeotdarc, andrea.bergamini, ezio.melotti, mark.dickinson, pitrou, python-dev, r.david.murray, rhettinger, skrah
Priority: normal Keywords: patch

Created on 2012-07-24 10:57 by andrea.bergamini, last changed 2022-04-11 14:57 by admin. This issue is now closed.

Files
File name Uploaded Description Edit
issue15438.patch mark.dickinson, 2013-01-27 10:48 review
issue15438_2.patch mark.dickinson, 2013-02-10 18:35 review
issue15438_3.patch mark.dickinson, 2013-02-10 18:36 review
Messages (31)
msg166275 - (view) Author: andrea bergamini (andrea.bergamini) Date: 2012-07-24 10:57
math.pow(43, 10) gives the wrong result: 21611482313284248.0
Instead, the build-in function 43**10 and pow(43, 10) give the correct result: 21611482313284249L.
This bug has been seen on ActivePython 2.5.1.1. Sorry no tests on recent versions.
msg166277 - (view) Author: Ezio Melotti (ezio.melotti) * (Python committer) Date: 2012-07-24 11:32
This is what I get on both 2.7 and 3.3:
>>> import math
>>> math.pow(43, 10)
2.161148231328425e+16
>>> pow(43, 10)
21611482313284249
>>> 43**10
21611482313284249
>>> int(math.pow(43, 10))
21611482313284248
msg166278 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2012-07-24 11:42
>>> (43**10).bit_length()
55
>>> sys.float_info.mant_dig
53

See http://docs.python.org/faq/design.html#why-are-floating-point-calculations-so-inaccurate
msg166283 - (view) Author: andrea bergamini (andrea.bergamini) Date: 2012-07-24 12:18
Ok, but math.pow IMPO has to be aligned to pow and built-in pow (**), otherwise this kind of "inaccuracies" can compromise the application behavior. I was using this funcion in a cryptographic mechanisms, and this issue resulted in a failure. 
Generally speaking, integer numbers should not be affected by inaccuracies. I mean, there's no floating point.
msg166284 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2012-07-24 12:30
If I understand correctly, the math module is providing C standard (Annex F) *floating point* mathematical functions.  Mark will have the definitive answer once he gets a chance to comment.  Perhaps a documentation clarification is in order on this point.
msg166285 - (view) Author: Stefan Krah (skrah) * (Python committer) Date: 2012-07-24 12:38
I think Serhiy has already explained that 43**10 is too large
to be represented exactly in 53-bit floating point arithmetic.

The math module wraps the floating point functions from the
C standard:

"It provides access to the mathematical functions defined by the C standard."

"Except when explicitly noted otherwise, all return values are floats."


So there's no bug, and the documentation looks fine to me, too.
msg166286 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2012-07-24 12:39
Well, the math.pow() doc could use a "seealso" pointing to the built-in pow() function perhaps.
msg166287 - (view) Author: Stefan Krah (skrah) * (Python committer) Date: 2012-07-24 12:45
How about changing the title to something like:

math -- 53-bit floating point arithmetic
msg166288 - (view) Author: Stefan Krah (skrah) * (Python committer) Date: 2012-07-24 12:46
"Title" referring to the section header of http://docs.python.org/dev/library/math.html ...
msg166289 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2012-07-24 12:53
That could help, but you easily miss the title when looking up the doc for a given function.

And since log2() already has a seealso for the corresponding int method, pow() could grow one as well.
(and in all honesty I don't know the difference between the "**" operator and the built-in pow() function :-))
msg166290 - (view) Author: andrea bergamini (andrea.bergamini) Date: 2012-07-24 13:02
Well, from a library I'm used to expect a good result or an exception. Not a value that differs from the correct of one unit! I agree with Antoine, the doc should warn about this behavior. I've lost a lot of time before discovering my application issue came from the py math library...
msg166293 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2012-07-24 13:49
Your problems didn't come from the "Python" math library, it came from the C math library that Python provides a wrapper for, which the documentation does clearly state.  And the result you got is accurate...for a floating point calculation.
msg166337 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2012-07-24 23:18
The math module is primarily about exposing the C floating point library functions.  Any integer arguments are converted to double.

We could add more docs but that usually doesn't help someone who already has an expectation that math.pow does the same thing as int ** int.
msg166370 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2012-07-25 08:59
Agreed that this is at worst a doc issue.

[Antoine]
> and in all honesty I don't know the difference between the "**" operator
> and the built-in pow() function :-)

None, as far as I know, apart from the pow function's ability to take a 3rd argument.  Both ultimately call PyNumber_Power, and the various type-specific __pow__ methods take over from there.  [+1 for removing pow from the builtins and shunting three-argument pow to the math module in Python 5000000.]
msg166398 - (view) Author: Ramchandra Apte (Ramchandra Apte) * Date: 2012-07-25 13:46
> [+1 for removing pow from the builtins and shunting three-argument pow to the math module in Python 5000000.]
Me too.
Anybody who uses pow with to arguments can use arg1**arg2
Anybody who uses pow with three is doing something mathematical and has most likely imported math already.
msg166402 - (view) Author: Stefan Krah (skrah) * (Python committer) Date: 2012-07-25 14:03
Ramchandra Apte <report@bugs.python.org> wrote:
> > [+1 for removing pow from the builtins and shunting three-argument pow to the math module in Python 5000000.]
> Anybody who uses pow with three is doing something mathematical and has most likely imported math already.

Wouldn't that reinforce the misconception that math is for arbitrary precision
number theoretical functions? The OP used it for cryptography.
msg166403 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2012-07-25 14:13
> Wouldn't that reinforce the misconception that math is for arbitrary
> precision number theoretical functions?

Perhaps.  We already have math.factorial, though;  adding math.powmod wouldn't seem so much of a stretch.  Just to be clear, I'm not seriously proposing this for any version of Python before 4.0; apologies for derailing the issue thread.
msg166476 - (view) Author: andrea bergamini (andrea.bergamini) Date: 2012-07-26 12:29
Ok guys, ticket closed, but I'm still confused: I'm not a Python expert, I've understood that math is a sort of wrapper of C math.h or something like this, but:
- I can't find any reason in using math.pow if I can get errors like the one explained.
- I've used math.h in my C++ code without having experienced any problem in that pow operation.
I'm surely missing something but I'm a bit confused...
msg166478 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2012-07-26 12:52
> - I can't find any reason in using math.pow if I can get errors like the one explained.

The reason is your intention to get the error.

>>> pow(-1, 0.5)
(6.123031769111886e-17+1j)
>>> math.pow(-1, 0.5)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: math domain error

> - I've used math.h in my C++ code without having experienced any problem in that pow operation.

What you get in C++ as result of pow(43, 10)?

Technically, in C++ you should use <cmath> header, not <math.h>.
msg166479 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2012-07-26 12:56
- I can't find any reason in using math.pow if I can get errors like the one explained.

Yep---don't use math.pow if you want *exact* integer results.  If you're doing numerical calculations and errors of around 1 part in 1 thousand million million are acceptable to you, then math.pow works just fine.  Just like the other math functions (math.log, math.exp, math.sin, etc.), it's using floating-point arithmetic, so converts its inputs to float and gives a float result.  Unlike Python ints, which have unbounded precision, Python floats have a fixed size (64 bits), so there are only finitely many values (less than 2**64) that can be represented exactly.  The fact is that the number you were expecting, 21611482313284249, isn't one of those numbers:  it *doesn't exist* as a float, because it's not exactly representable in the usual 64-bit floating-point type that Python uses internally.

- I've used math.h in my C++ code without having experienced any problem in that pow operation.

I'd be quite surprised if this were true:  if you're using the double type with C or C++, and the pow function from math.h / cmath, you should expect to see *exactly* the same issues.  With the 'long double' type, you may get a little more precision (depending on the platform), but that just delays the point at which those issues would appear.

By the way, don't close the issue just yet!  There's still ongoing discussion here about whether
there's potential for a documentation improvement.
msg166493 - (view) Author: Amaury Forgeot d'Arc (amaury.forgeotdarc) * (Python committer) Date: 2012-07-26 16:48
C comparison rules are different from Python's.
In the program below (which outputs 1), the mixed comparison will first convert the literal to a double, and lost some precision.
Python does the opposite: the (imprecise) float is converted to a long, so all digits are compared.


#include <math.h>
#include <stdio.h>
int main()
{
    printf("result: %d\n", (pow(43,10) == 21611482313284249));
}
msg166504 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2012-07-26 19:33
Ah yes; a comparison like that could indeed give the impression that C/C++ was computing things exactly. :-)
msg166693 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2012-07-28 21:08
Changing the incredible issue title :-)
msg179317 - (view) Author: Ezio Melotti (ezio.melotti) * (Python committer) Date: 2013-01-08 07:17
> Well, the math.pow() doc could use a "seealso" pointing to the built-in
> pow() function perhaps.

Pointing to ``**`` is probably better.
I think that a simple note that mentions the ** operator and when it's better to use it (and possibly the limitations of math.pow) is enough.
Anyone wants to suggest a specific wording?
msg180760 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2013-01-27 10:48
> Anyone wants to suggest a specific wording?

How about the attached patch?
msg180773 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2013-01-27 15:34
I don't think it should be .. note, but otherwise it looks fine to me.
msg181835 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2013-02-10 18:35
Updated patch.  Thanks Ezio and David for reviewing.
msg181836 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2013-02-10 18:36
Whoops.  Removing a bonus non-grammatical 'function'.
msg182233 - (view) Author: Ezio Melotti (ezio.melotti) * (Python committer) Date: 2013-02-16 17:33
LGTM.
(Maybe build the doc and double check that all the links are correct before committing.)
msg182708 - (view) Author: Roundup Robot (python-dev) (Python triager) Date: 2013-02-23 02:56
New changeset ad0712f4b3e0 by Ezio Melotti in branch '2.7':
#15438: add a note to math.pow() that suggests using **/pow() for integers.  Patch by Mark Dickinson.
http://hg.python.org/cpython/rev/ad0712f4b3e0

New changeset 7d95a0aa6b5a by Ezio Melotti in branch '3.2':
#15438: add a note to math.pow() that suggests using **/pow() for integers.  Patch by Mark Dickinson.
http://hg.python.org/cpython/rev/7d95a0aa6b5a

New changeset a305901366a6 by Ezio Melotti in branch '3.3':
#15438: merge with 3.2.
http://hg.python.org/cpython/rev/a305901366a6

New changeset e0f940829eb6 by Ezio Melotti in branch 'default':
#15438: merge with 3.3.
http://hg.python.org/cpython/rev/e0f940829eb6
msg182744 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2013-02-23 13:59
Thanks, Ezio;  I didn't get around to dealing with this as quickly as I meant to.
History
Date User Action Args
2022-04-11 14:57:33adminsetgithub: 59643
2013-02-23 13:59:12mark.dickinsonsetmessages: + msg182744
2013-02-23 02:56:51ezio.melottisetstatus: open -> closed
resolution: fixed
stage: commit review -> resolved
2013-02-23 02:56:03python-devsetnosy: + python-dev
messages: + msg182708
2013-02-16 17:33:05ezio.melottisetmessages: + msg182233
stage: needs patch -> commit review
2013-02-10 18:36:42mark.dickinsonsetfiles: + issue15438_3.patch

messages: + msg181836
2013-02-10 18:35:29mark.dickinsonsetpriority: low -> normal
assignee: mark.dickinson
messages: + msg181835

files: + issue15438_2.patch
2013-01-27 15:34:55r.david.murraysetmessages: + msg180773
2013-01-27 10:48:36mark.dickinsonsetfiles: + issue15438.patch
keywords: + patch
messages: + msg180760
2013-01-08 07:17:14ezio.melottisetversions: + Python 2.7, Python 3.2, Python 3.3, Python 3.4
resolution: not a bug -> (no value)
messages: + msg179317

assignee: rhettinger -> (no value)
type: behavior -> enhancement
stage: needs patch
2013-01-07 16:27:15serhiy.storchakasetnosy: - serhiy.storchaka
2012-07-28 21:08:20pitrousetmessages: + msg166693
title: Incredible issue in math.pow -> document that math.pow is inappropriate for integers
2012-07-26 19:33:35mark.dickinsonsetmessages: + msg166504
2012-07-26 16:48:36amaury.forgeotdarcsetnosy: + amaury.forgeotdarc
messages: + msg166493
2012-07-26 12:56:19mark.dickinsonsetstatus: closed -> open

messages: + msg166479
2012-07-26 12:52:59serhiy.storchakasetmessages: + msg166478
2012-07-26 12:29:54andrea.bergaminisetstatus: open -> closed

messages: + msg166476
2012-07-25 14:13:10mark.dickinsonsetmessages: + msg166403
2012-07-25 14:03:26skrahsetmessages: + msg166402
2012-07-25 13:46:33Ramchandra Aptesetnosy: + Ramchandra Apte
messages: + msg166398
2012-07-25 08:59:07mark.dickinsonsetmessages: + msg166370
2012-07-24 23:18:41rhettingersetresolution: not a bug
components: + Documentation, - Library (Lib)
2012-07-24 23:18:15rhettingersetpriority: normal -> low

nosy: + rhettinger
messages: + msg166337

assignee: rhettinger
2012-07-24 13:49:28r.david.murraysetmessages: + msg166293
2012-07-24 13:02:50andrea.bergaminisetmessages: + msg166290
2012-07-24 12:53:20pitrousetmessages: + msg166289
2012-07-24 12:46:17skrahsetmessages: + msg166288
2012-07-24 12:45:06skrahsetmessages: + msg166287
2012-07-24 12:39:59pitrousetnosy: + pitrou
messages: + msg166286
2012-07-24 12:38:19skrahsetmessages: + msg166285
2012-07-24 12:30:30r.david.murraysetnosy: + r.david.murray
messages: + msg166284
2012-07-24 12:18:41andrea.bergaminisetmessages: + msg166283
2012-07-24 11:42:46serhiy.storchakasetnosy: + serhiy.storchaka
messages: + msg166278
2012-07-24 11:32:57ezio.melottisetnosy: + skrah, ezio.melotti
messages: + msg166277
2012-07-24 11:08:12ned.deilysetnosy: + mark.dickinson
2012-07-24 10:57:49andrea.bergaminicreate