Skip to content

Commit 7852616

Browse files
committed
Return reasonable results for math.log(long) and math.log10(long) (we were
getting Infs, NaNs, or nonsense in 2.1 and before; in yesterday's CVS we were getting OverflowError; but these functions always make good sense for positive arguments, no matter how large).
1 parent 63c9453 commit 7852616

File tree

3 files changed

+104
-7
lines changed

3 files changed

+104
-7
lines changed

Lib/test/test_long.py

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from test_support import verify, verbose, TestFailed
1+
from test_support import verify, verbose, TestFailed, fcmp
22
from string import join
33
from random import random, randint
44

@@ -353,9 +353,7 @@ def test_float_overflow():
353353
"1. / huge", "huge / 1.", "1. / mhuge", "mhuge / 1.",
354354
"1. ** huge", "huge ** 1.", "1. ** mhuge", "mhuge ** 1.",
355355
"math.sin(huge)", "math.sin(mhuge)",
356-
"math.log(huge)", "math.log(mhuge)", # should do better
357356
"math.sqrt(huge)", "math.sqrt(mhuge)", # should do better
358-
"math.log10(huge)", "math.log10(mhuge)", # should do better
359357
"math.floor(huge)", "math.floor(mhuge)"]:
360358

361359
try:
@@ -364,6 +362,41 @@ def test_float_overflow():
364362
pass
365363
else:
366364
raise TestFailed("expected OverflowError from %s" % test)
365+
366+
# ---------------------------------------------- test huge log and log10
367+
368+
def test_logs():
369+
import math
370+
371+
if verbose:
372+
print "log and log10"
373+
374+
LOG10E = math.log10(math.e)
375+
376+
for exp in range(10) + [100, 1000, 10000]:
377+
value = 10 ** exp
378+
log10 = math.log10(value)
379+
verify(fcmp(log10, exp) == 0)
380+
381+
# log10(value) == exp, so log(value) == log10(value)/log10(e) ==
382+
# exp/LOG10E
383+
expected = exp / LOG10E
384+
log = math.log(value)
385+
verify(fcmp(log, expected) == 0)
386+
387+
for bad in -(1L << 10000), -2L, 0L:
388+
try:
389+
math.log(bad)
390+
raise TestFailed("expected ValueError from log(<= 0)")
391+
except ValueError:
392+
pass
393+
394+
try:
395+
math.log10(bad)
396+
raise TestFailed("expected ValueError from log10(<= 0)")
397+
except ValueError:
398+
pass
399+
367400
# ---------------------------------------------------------------- do it
368401

369402
test_division()
@@ -372,3 +405,4 @@ def test_float_overflow():
372405
test_misc()
373406
test_auto_overflow()
374407
test_float_overflow()
408+
test_logs()

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ Core
8181

8282
Library
8383

84+
- math.log and math.log10 now return sensible results for even huge
85+
long arguments. For example, math.log10(10 ** 10000) ~= 10000.0.
86+
8487
- A new function, imp.lock_held(), returns 1 when the import lock is
8588
currently held. See the docs for the imp module.
8689

Modules/mathmodule.c

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/* Math module -- standard C math library functions, pi and e */
22

33
#include "Python.h"
4+
#include "longintrepr.h"
45

56
#ifndef _MSC_VER
67
#ifndef __STDC__
@@ -136,10 +137,6 @@ FUNC2(fmod, fmod,
136137
" x % y may differ.")
137138
FUNC2(hypot, hypot,
138139
"hypot(x,y)\n\nReturn the Euclidean distance, sqrt(x*x + y*y).")
139-
FUNC1(log, log,
140-
"log(x)\n\nReturn the natural logarithm of x.")
141-
FUNC1(log10, log10,
142-
"log10(x)\n\nReturn the base-10 logarithm of x.")
143140
#ifdef MPW_3_1 /* This hack is needed for MPW 3.1 but not for 3.2 ... */
144141
FUNC2(pow, power,
145142
"pow(x,y)\n\nReturn x**y (x to the power of y).")
@@ -231,6 +228,69 @@ static char math_modf_doc [] =
231228
"Return the fractional and integer parts of x. Both results carry the sign\n"
232229
"of x. The integer part is returned as a real.";
233230

231+
/* A decent logarithm is easy to compute even for huge longs, but libm can't
232+
do that by itself -- loghelper can. func is log or log10, and name is
233+
"log" or "log10". Note that overflow isn't possible: a long can contain
234+
no more than INT_MAX * SHIFT bits, so has value certainly less than
235+
2**(2**64 * 2**16) == 2**2**80, and log2 of that is 2**80, which is
236+
small enough to fit in an IEEE single. log and log10 are even smaller.
237+
*/
238+
239+
static PyObject*
240+
loghelper(PyObject* args, double (*func)(double), char *name)
241+
{
242+
PyObject *arg;
243+
char format[16];
244+
245+
/* See whether this is a long. */
246+
format[0] = 'O';
247+
format[1] = ':';
248+
strcpy(format + 2, name);
249+
if (! PyArg_ParseTuple(args, format, &arg))
250+
return NULL;
251+
252+
/* If it is long, do it ourselves. */
253+
if (PyLong_Check(arg)) {
254+
double x;
255+
int e;
256+
x = _PyLong_AsScaledDouble(arg, &e);
257+
if (x <= 0.0) {
258+
PyErr_SetString(PyExc_ValueError,
259+
"math domain error");
260+
return NULL;
261+
}
262+
/* Value is ~= x * 2**(e*SHIFT), so the log ~=
263+
log(x) + log(2) * e * SHIFT.
264+
CAUTION: e*SHIFT may overflow using int arithmetic,
265+
so force use of double. */
266+
x = func(x) + func(2.0) * (double)e * (double)SHIFT;
267+
return PyFloat_FromDouble(x);
268+
}
269+
270+
/* Else let libm handle it by itself. */
271+
format[0] = 'd';
272+
return math_1(args, func, format);
273+
}
274+
275+
static PyObject *
276+
math_log(PyObject *self, PyObject *args)
277+
{
278+
return loghelper(args, log, "log");
279+
}
280+
281+
static char math_log_doc[] =
282+
"log(x) -> the natural logarithm (base e) of x.";
283+
284+
static PyObject *
285+
math_log10(PyObject *self, PyObject *args)
286+
{
287+
return loghelper(args, log10, "log10");
288+
}
289+
290+
static char math_log10_doc[] =
291+
"log10(x) -> the base 10 logarithm of x.";
292+
293+
234294
static PyMethodDef math_methods[] = {
235295
{"acos", math_acos, METH_VARARGS, math_acos_doc},
236296
{"asin", math_asin, METH_VARARGS, math_asin_doc},

0 commit comments

Comments
 (0)