summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTim Peters <tim.peters@gmail.com>2001-09-05 00:53:45 (GMT)
committerTim Peters <tim.peters@gmail.com>2001-09-05 00:53:45 (GMT)
commit785261684e0e660dcdce48daf683cec541f4a8f2 (patch)
tree93a48bc9c7d2bf9725406c756a5535f298e583a0
parent63c945392917cd9c228a613d2e965500452bb92e (diff)
downloadcpython-785261684e0e660dcdce48daf683cec541f4a8f2.zip
cpython-785261684e0e660dcdce48daf683cec541f4a8f2.tar.gz
cpython-785261684e0e660dcdce48daf683cec541f4a8f2.tar.bz2
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).
-rw-r--r--Lib/test/test_long.py40
-rw-r--r--Misc/NEWS3
-rw-r--r--Modules/mathmodule.c68
3 files changed, 104 insertions, 7 deletions
diff --git a/Lib/test/test_long.py b/Lib/test/test_long.py
index 104b086..573ef75 100644
--- a/Lib/test/test_long.py
+++ b/Lib/test/test_long.py
@@ -1,4 +1,4 @@
-from test_support import verify, verbose, TestFailed
+from test_support import verify, verbose, TestFailed, fcmp
from string import join
from random import random, randint
@@ -353,9 +353,7 @@ def test_float_overflow():
"1. / huge", "huge / 1.", "1. / mhuge", "mhuge / 1.",
"1. ** huge", "huge ** 1.", "1. ** mhuge", "mhuge ** 1.",
"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.log10(huge)", "math.log10(mhuge)", # should do better
"math.floor(huge)", "math.floor(mhuge)"]:
try:
@@ -364,6 +362,41 @@ def test_float_overflow():
pass
else:
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
test_division()
@@ -372,3 +405,4 @@ test_format()
test_misc()
test_auto_overflow()
test_float_overflow()
+test_logs()
diff --git a/Misc/NEWS b/Misc/NEWS
index 7d44a59..5e763c0 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -81,6 +81,9 @@ Core
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
currently held. See the docs for the imp module.
diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c
index f715418..eef8b78 100644
--- a/Modules/mathmodule.c
+++ b/Modules/mathmodule.c
@@ -1,6 +1,7 @@
/* Math module -- standard C math library functions, pi and e */
#include "Python.h"
+#include "longintrepr.h"
#ifndef _MSC_VER
#ifndef __STDC__
@@ -136,10 +137,6 @@ FUNC2(fmod, fmod,
" x % y may differ.")
FUNC2(hypot, hypot,
"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 ... */
FUNC2(pow, power,
"pow(x,y)\n\nReturn x**y (x to the power of y).")
@@ -231,6 +228,69 @@ static char math_modf_doc [] =
"Return the fractional and integer parts of x. Both results carry the sign\n"
"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[] = {
{"acos", math_acos, METH_VARARGS, math_acos_doc},
{"asin", math_asin, METH_VARARGS, math_asin_doc},