mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
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).
This commit is contained in:
parent
63c9453929
commit
785261684e
3 changed files with 104 additions and 7 deletions
|
@ -1,4 +1,4 @@
|
||||||
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():
|
||||||
"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():
|
||||||
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()
|
||||||
test_misc()
|
test_misc()
|
||||||
test_auto_overflow()
|
test_auto_overflow()
|
||||||
test_float_overflow()
|
test_float_overflow()
|
||||||
|
test_logs()
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
/* 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,
|
||||||
" 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 [] =
|
||||||
"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},
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue