From 36f6e2c9e0cfd1bab83d6ae1d192d4951ccc0160 Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Sun, 13 Oct 2013 10:55:15 +0100 Subject: [PATCH] Issue #18739: Fix inconsistent results from math.log(n) and math.log(long(n)) --- Lib/test/test_math.py | 6 ++++++ Misc/NEWS | 3 +++ Modules/mathmodule.c | 34 ++++++++++++++++++++++------------ 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index ac4475e2e53..a8677ce33df 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -599,6 +599,9 @@ class MathTests(unittest.TestCase): self.assertEqual(math.log(INF), INF) self.assertRaises(ValueError, math.log, NINF) self.assertTrue(math.isnan(math.log(NAN))) + # Log values should match for int and long (issue #18739). + for n in range(1, 1000): + self.assertEqual(math.log(n), math.log(long(n))) def testLog1p(self): self.assertRaises(TypeError, math.log1p) @@ -621,6 +624,9 @@ class MathTests(unittest.TestCase): self.assertEqual(math.log(INF), INF) self.assertRaises(ValueError, math.log10, NINF) self.assertTrue(math.isnan(math.log10(NAN))) + # Log values should match for int and long (issue #18739). + for n in range(1, 1000): + self.assertEqual(math.log10(n), math.log10(long(n))) def testModf(self): self.assertRaises(TypeError, math.modf) diff --git a/Misc/NEWS b/Misc/NEWS index b7402e10c9f..1500b15e960 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -9,6 +9,9 @@ What's New in Python 2.7.6? Core and Builtins ----------------- +- Issue #18739: Fix an inconsistency between math.log(n) and math.log(long(n)); + the results could be off from one another by a ulp or two. + - Issue #13461: Fix a crash in the "replace" error handler on 64-bit platforms. Patch by Yogesh Chaudhari. diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index cd74b0dc677..845fb9bced8 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -1277,23 +1277,33 @@ loghelper(PyObject* arg, double (*func)(double), char *funcname) { /* If it is long, do it ourselves. */ if (PyLong_Check(arg)) { - double x; + double x, result; Py_ssize_t e; - x = _PyLong_Frexp((PyLongObject *)arg, &e); - if (x == -1.0 && PyErr_Occurred()) - return NULL; - if (x <= 0.0) { + + /* Negative or zero inputs give a ValueError. */ + if (Py_SIZE(arg) <= 0) { PyErr_SetString(PyExc_ValueError, "math domain error"); return NULL; } - /* Special case for log(1), to make sure we get an - exact result there. */ - if (e == 1 && x == 0.5) - return PyFloat_FromDouble(0.0); - /* Value is ~= x * 2**e, so the log ~= log(x) + log(2) * e. */ - x = func(x) + func(2.0) * e; - return PyFloat_FromDouble(x); + + x = PyLong_AsDouble(arg); + if (x == -1.0 && PyErr_Occurred()) { + if (!PyErr_ExceptionMatches(PyExc_OverflowError)) + return NULL; + /* Here the conversion to double overflowed, but it's possible + to compute the log anyway. Clear the exception and continue. */ + PyErr_Clear(); + x = _PyLong_Frexp((PyLongObject *)arg, &e); + if (x == -1.0 && PyErr_Occurred()) + return NULL; + /* Value is ~= x * 2**e, so the log ~= log(x) + log(2) * e. */ + result = func(x) + func(2.0) * e; + } + else + /* Successfully converted x to a double. */ + result = func(x); + return PyFloat_FromDouble(result); } /* Else let libm handle it by itself. */