classification
Title: math.log, log10 inconsistency
Type: Stage:
Components: Documentation Versions: Python 3.1, Python 3.2, Python 2.7, Python 2.6
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: georg.brandl Nosy List: georg.brandl, mark.dickinson, steve21, terry.reedy, tim.peters
Priority: normal Keywords:

Created on 2009-08-23 11:56 by steve21, last changed 2009-09-01 07:54 by georg.brandl. This issue is now closed.

Messages (7)
msg91886 - (view) Author: Steve (steve21) Date: 2009-08-23 11:56
$ python3.1
Python 3.1 (r31:73572, Jul  6 2009, 21:21:12) 
[GCC 4.4.0 20090506 (Red Hat 4.4.0-4)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import math
>>> math.log10(1000)
3.0
>>> math.log(1000, 10)
2.9999999999999996

You would expect the results to be the same.
Internally math.log() could call math.log10() when base==10. That would
ensure they are consistent.
msg91888 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2009-08-23 15:22
Well, that's floating-point arithmetic for you.  log(x, y) simply computes 
log(x)/log(y) behind the scenes; since both log computations and the 
floating-point division can introduce errors, the result will frequently 
not be correctly rounded.

I don't really see the benefit of special-casing log(x, 10).  In what 
circumstances does it matter that log(x, 10) != log10(x)?  I could 
understand people being upset that log(10**n, 10) doesn't return n 
exactly, but that's what log10 is there for.

See also the discussion in issue 3724.
msg91900 - (view) Author: Steve (steve21) Date: 2009-08-24 02:04
Mark,
"... that's what log10 is there for". That would be a good point if the
documentation said that. However, all the docs for log10 say is:

math.log10(x)
    Return the base-10 logarithm of x.

So we have a python function log10() which looks like it is redundant
since we can use log(a, 10) instead. But it actually functions
differently to log(a, 10), and the Python user would never know this
from looking at the documentation.

I think Tim Peters missed one important guideline in his "The Zen of
Python". The principle of least astonishment (or surprise) - when two
elements of an interface conflict, or are ambiguous, the behaviour
should be that which will least surprise the human user or programmer at
the time the conflict arises.

Its easy for the python developer to ignore this guideline. They know
the implementation, and are rarely surprised by inconsistent behaviour
since they have seen it before, or even created it without documenting it.

If Python functions are inconsistent then I think they should either be
made consistent, or if that's not possible they should be clearly
documented as being inconsistent.

The docs for log(x[, base]) could be improved to say:
"this function preserves the consistency of log(a,b) == log(a)/log(b)
but breaks consistency with log(a,10) == log10(a)"

The docs for log10(x) could be improved to say:
"this function gives the correct result for powers of 10,
but breaks consistency with log(a,10) == log10(a)"
msg91918 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2009-08-24 13:09
> If Python functions are inconsistent then I think they should either 
be
> made consistent, or if that's not possible they should be clearly
> documented as being inconsistent.

I think 'inconsistent' is a bit strong for what happens here.  In my 
view, the expressions log10(x) and log(x, 10) behave consistently:  they 
both give (good) floating-point approximations to the base-10 logarithm 
of x.  However, since they use slightly different methods to get those 
approximations, the approximations are not identical.

Would you also say that atan2(y, x) is inconsistent with atan(y/x)  (for 
positive x, y, say), because they give minutely different results in 
some cases?

> The docs for log10(x) could be improved to say:
> "this function gives the correct result for powers of 10,
> but breaks consistency with log(a,10) == log10(a)"

I find this unnecessarily negative.  I propose instead to leave the log 
documentation as it is, and add something like the following to the 
log10 documentation, as an explanation of why log10 is still valuable in 
the presence of the two-argument log function:

"""Since this function directly wraps the platform log10 function, 
``log10(x)`` will usually (depending on the platform) be faster and more 
accurate than the mathematically equivalent ``log(x, 10)``."""

Note that historically, the two-argument version of math.log appeared 
long after math.log10 did;  this might help explain the apparent 
redundancy.  (I'd actually argue that the two-argument log should not 
have been added in the first place:  it doesn't wrap a standard C math 
function, and it's really no different from log(x)/log(y), except that 
having it as a built-in library function gives the illusion that it 
might be more accurate than it actually is.  However, that's academic 
now.)

One possible 'fix' for this situation does occur to me:  instead of 
computing log(x, y) internally as log(x)/log(y), compute it as 
log10(x)/log10(y).  This at least doesn't involve extensive changes or 
additions to the code.  I don't think it really makes any practical 
difference, except perhaps in reducing the number of bug reports like 
this one.  And it won't help make log2(x) == log(x, 2) if/when the C99 
log2 function is added to the math library, either.  I'd be -0 on making 
this change.
msg91919 - (view) Author: Tim Peters (tim.peters) * (Python committer) Date: 2009-08-24 13:25
I wasn't keen to add the 2-argument log() extension either.  However, I
bet it would help if the docs for that were changed to explain that
log(x, base) is just a convenient shorthand for computing
log(x)/log(base), and therefore may be a little less accurate than a
function that directly computed the logarithm wrt the given base.
msg92042 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2009-08-28 20:11
Reopening as doc issue. This is at least the second time this issue has
been reported (see #3724), with some agreement on doc tweak, but not done.

Specific suggestions for math module doc, 9.2.2:

math.log(x[, base]) 
Return the logarithm of x to the given base. 

add: ", calculated as log(x)/log(base)" before period.

[This could be removed if better method ever implemented.]

math.log10(x) 
Return the base-10 logarithm of x.

add: "This is usually more accurate than log(x, 10)."
msg92126 - (view) Author: Georg Brandl (georg.brandl) * (Python committer) Date: 2009-09-01 07:54
Okay, I made changes along Terry' suggestions in r74617.
History
Date User Action Args
2009-09-01 07:54:00georg.brandlsetstatus: open -> closed
resolution: fixed
messages: + msg92126
2009-08-28 20:11:40terry.reedysetstatus: closed -> open

assignee: georg.brandl
components: + Documentation, - Library (Lib)
versions: + Python 2.6, Python 2.7, Python 3.2
nosy: + terry.reedy, georg.brandl

messages: + msg92042
resolution: not a bug -> (no value)
2009-08-24 13:25:09tim.peterssetnosy: + tim.peters
messages: + msg91919
2009-08-24 13:09:45mark.dickinsonsetmessages: + msg91918
2009-08-24 02:04:21steve21setmessages: + msg91900
2009-08-23 21:47:50loewissetstatus: open -> closed
resolution: not a bug
2009-08-23 15:22:51mark.dickinsonsetnosy: + mark.dickinson
messages: + msg91888
2009-08-23 11:56:16steve21create