Commit 12d8c659 authored by Tim Peters's avatar Tim Peters

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).
parent 9109832d
from test_support import verify, verbose, TestFailed from test_support import verify, verbose, TestFailed, fcmp
from string import join from string import join
from random import random, randint from random import random, randint
...@@ -353,9 +353,7 @@ def test_float_overflow(): ...@@ -353,9 +353,7 @@ def test_float_overflow():
"1. / huge", "huge / 1.", "1. / mhuge", "mhuge / 1.", "1. / huge", "huge / 1.", "1. / mhuge", "mhuge / 1.",
"1. ** huge", "huge ** 1.", "1. ** mhuge", "mhuge ** 1.", "1. ** huge", "huge ** 1.", "1. ** mhuge", "mhuge ** 1.",
"math.sin(huge)", "math.sin(mhuge)", "math.sin(huge)", "math.sin(mhuge)",
"math.log(huge)", "math.log(mhuge)", # should do better
"math.sqrt(huge)", "math.sqrt(mhuge)", # should do better "math.sqrt(huge)", "math.sqrt(mhuge)", # should do better
"math.log10(huge)", "math.log10(mhuge)", # should do better
"math.floor(huge)", "math.floor(mhuge)"]: "math.floor(huge)", "math.floor(mhuge)"]:
try: try:
...@@ -364,6 +362,41 @@ def test_float_overflow(): ...@@ -364,6 +362,41 @@ def test_float_overflow():
pass pass
else: else:
raise TestFailed("expected OverflowError from %s" % test) raise TestFailed("expected OverflowError from %s" % test)
# ---------------------------------------------- test huge log and log10
def test_logs():
import math
if verbose:
print "log and log10"
LOG10E = math.log10(math.e)
for exp in range(10) + [100, 1000, 10000]:
value = 10 ** exp
log10 = math.log10(value)
verify(fcmp(log10, exp) == 0)
# log10(value) == exp, so log(value) == log10(value)/log10(e) ==
# exp/LOG10E
expected = exp / LOG10E
log = math.log(value)
verify(fcmp(log, expected) == 0)
for bad in -(1L << 10000), -2L, 0L:
try:
math.log(bad)
raise TestFailed("expected ValueError from log(<= 0)")
except ValueError:
pass
try:
math.log10(bad)
raise TestFailed("expected ValueError from log10(<= 0)")
except ValueError:
pass
# ---------------------------------------------------------------- do it # ---------------------------------------------------------------- do it
test_division() test_division()
...@@ -372,3 +405,4 @@ test_format() ...@@ -372,3 +405,4 @@ test_format()
test_misc() test_misc()
test_auto_overflow() test_auto_overflow()
test_float_overflow() test_float_overflow()
test_logs()
...@@ -81,6 +81,9 @@ Core ...@@ -81,6 +81,9 @@ Core
Library Library
- math.log and math.log10 now return sensible results for even huge
long arguments. For example, math.log10(10 ** 10000) ~= 10000.0.
- A new function, imp.lock_held(), returns 1 when the import lock is - A new function, imp.lock_held(), returns 1 when the import lock is
currently held. See the docs for the imp module. currently held. See the docs for the imp module.
......
/* Math module -- standard C math library functions, pi and e */ /* Math module -- standard C math library functions, pi and e */
#include "Python.h" #include "Python.h"
#include "longintrepr.h"
#ifndef _MSC_VER #ifndef _MSC_VER
#ifndef __STDC__ #ifndef __STDC__
...@@ -136,10 +137,6 @@ FUNC2(fmod, fmod, ...@@ -136,10 +137,6 @@ FUNC2(fmod, fmod,
" x % y may differ.") " x % y may differ.")
FUNC2(hypot, hypot, FUNC2(hypot, hypot,
"hypot(x,y)\n\nReturn the Euclidean distance, sqrt(x*x + y*y).") "hypot(x,y)\n\nReturn the Euclidean distance, sqrt(x*x + y*y).")
FUNC1(log, log,
"log(x)\n\nReturn the natural logarithm of x.")
FUNC1(log10, log10,
"log10(x)\n\nReturn the base-10 logarithm of x.")
#ifdef MPW_3_1 /* This hack is needed for MPW 3.1 but not for 3.2 ... */ #ifdef MPW_3_1 /* This hack is needed for MPW 3.1 but not for 3.2 ... */
FUNC2(pow, power, FUNC2(pow, power,
"pow(x,y)\n\nReturn x**y (x to the power of y).") "pow(x,y)\n\nReturn x**y (x to the power of y).")
...@@ -231,6 +228,69 @@ static char math_modf_doc [] = ...@@ -231,6 +228,69 @@ static char math_modf_doc [] =
"Return the fractional and integer parts of x. Both results carry the sign\n" "Return the fractional and integer parts of x. Both results carry the sign\n"
"of x. The integer part is returned as a real."; "of x. The integer part is returned as a real.";
/* A decent logarithm is easy to compute even for huge longs, but libm can't
do that by itself -- loghelper can. func is log or log10, and name is
"log" or "log10". Note that overflow isn't possible: a long can contain
no more than INT_MAX * SHIFT bits, so has value certainly less than
2**(2**64 * 2**16) == 2**2**80, and log2 of that is 2**80, which is
small enough to fit in an IEEE single. log and log10 are even smaller.
*/
static PyObject*
loghelper(PyObject* args, double (*func)(double), char *name)
{
PyObject *arg;
char format[16];
/* See whether this is a long. */
format[0] = 'O';
format[1] = ':';
strcpy(format + 2, name);
if (! PyArg_ParseTuple(args, format, &arg))
return NULL;
/* If it is long, do it ourselves. */
if (PyLong_Check(arg)) {
double x;
int e;
x = _PyLong_AsScaledDouble(arg, &e);
if (x <= 0.0) {
PyErr_SetString(PyExc_ValueError,
"math domain error");
return NULL;
}
/* Value is ~= x * 2**(e*SHIFT), so the log ~=
log(x) + log(2) * e * SHIFT.
CAUTION: e*SHIFT may overflow using int arithmetic,
so force use of double. */
x = func(x) + func(2.0) * (double)e * (double)SHIFT;
return PyFloat_FromDouble(x);
}
/* Else let libm handle it by itself. */
format[0] = 'd';
return math_1(args, func, format);
}
static PyObject *
math_log(PyObject *self, PyObject *args)
{
return loghelper(args, log, "log");
}
static char math_log_doc[] =
"log(x) -> the natural logarithm (base e) of x.";
static PyObject *
math_log10(PyObject *self, PyObject *args)
{
return loghelper(args, log10, "log10");
}
static char math_log10_doc[] =
"log10(x) -> the base 10 logarithm of x.";
static PyMethodDef math_methods[] = { static PyMethodDef math_methods[] = {
{"acos", math_acos, METH_VARARGS, math_acos_doc}, {"acos", math_acos, METH_VARARGS, math_acos_doc},
{"asin", math_asin, METH_VARARGS, math_asin_doc}, {"asin", math_asin, METH_VARARGS, math_asin_doc},
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment