Skip to content

Commit ca00e3b

Browse files
gh-120950: Fix overflow in math.log() with large int-like argument
Handling of arbitrary large int-like argument is now consistent with handling arbitrary large int arguments.
1 parent 769aea3 commit ca00e3b

File tree

2 files changed

+60
-28
lines changed

2 files changed

+60
-28
lines changed

Lib/test/test_math.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1208,6 +1208,11 @@ def testLog(self):
12081208
self.ftest('log(10**40, 10**20)', math.log(10**40, 10**20), 2)
12091209
self.ftest('log(10**1000)', math.log(10**1000),
12101210
2302.5850929940457)
1211+
self.ftest('log(MyIndexable(32), MyIndexable(2))',
1212+
math.log(MyIndexable(32), MyIndexable(2)), 5)
1213+
self.ftest('log(MyIndexable(10**1000))',
1214+
math.log(MyIndexable(10**1000)),
1215+
2302.5850929940457)
12111216
self.assertRaises(ValueError, math.log, -1.5)
12121217
self.assertRaises(ValueError, math.log, -10**1000)
12131218
self.assertRaises(ValueError, math.log, 10, -10)
@@ -1230,11 +1235,13 @@ def testLog2(self):
12301235
self.assertEqual(math.log2(1), 0.0)
12311236
self.assertEqual(math.log2(2), 1.0)
12321237
self.assertEqual(math.log2(4), 2.0)
1238+
self.assertEqual(math.log2(MyIndexable(4)), 2.0)
12331239

12341240
# Large integer values
12351241
self.assertEqual(math.log2(2**1023), 1023.0)
12361242
self.assertEqual(math.log2(2**1024), 1024.0)
12371243
self.assertEqual(math.log2(2**2000), 2000.0)
1244+
self.assertEqual(math.log2(MyIndexable(2**2000)), 2000.0)
12381245

12391246
self.assertRaises(ValueError, math.log2, -1.5)
12401247
self.assertRaises(ValueError, math.log2, NINF)
@@ -1255,6 +1262,9 @@ def testLog10(self):
12551262
self.ftest('log10(1)', math.log10(1), 0)
12561263
self.ftest('log10(10)', math.log10(10), 1)
12571264
self.ftest('log10(10**1000)', math.log10(10**1000), 1000.0)
1265+
self.ftest('log10(MyIndexable(10))', math.log10(MyIndexable(10)), 1)
1266+
self.ftest('log10(MyIndexable(10**1000))',
1267+
math.log10(MyIndexable(10**1000)), 1000.0)
12581268
self.assertRaises(ValueError, math.log10, -1.5)
12591269
self.assertRaises(ValueError, math.log10, -10**1000)
12601270
self.assertRaises(ValueError, math.log10, NINF)

Modules/mathmodule.c

Lines changed: 50 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2217,41 +2217,63 @@ math_modf_impl(PyObject *module, double x)
22172217
in that int is larger than PY_SSIZE_T_MAX. */
22182218

22192219
static PyObject*
2220-
loghelper(PyObject* arg, double (*func)(double))
2220+
loghelper_int(PyObject* arg, double (*func)(double))
22212221
{
22222222
/* If it is int, do it ourselves. */
2223-
if (PyLong_Check(arg)) {
2224-
double x, result;
2225-
Py_ssize_t e;
2223+
double x, result;
2224+
Py_ssize_t e;
22262225

2227-
/* Negative or zero inputs give a ValueError. */
2228-
if (!_PyLong_IsPositive((PyLongObject *)arg)) {
2229-
PyErr_SetString(PyExc_ValueError,
2230-
"math domain error");
2231-
return NULL;
2232-
}
2226+
/* Negative or zero inputs give a ValueError. */
2227+
if (!_PyLong_IsPositive((PyLongObject *)arg)) {
2228+
PyErr_SetString(PyExc_ValueError,
2229+
"math domain error");
2230+
return NULL;
2231+
}
22332232

2234-
x = PyLong_AsDouble(arg);
2235-
if (x == -1.0 && PyErr_Occurred()) {
2236-
if (!PyErr_ExceptionMatches(PyExc_OverflowError))
2237-
return NULL;
2238-
/* Here the conversion to double overflowed, but it's possible
2239-
to compute the log anyway. Clear the exception and continue. */
2240-
PyErr_Clear();
2241-
x = _PyLong_Frexp((PyLongObject *)arg, &e);
2242-
if (x == -1.0 && PyErr_Occurred())
2243-
return NULL;
2244-
/* Value is ~= x * 2**e, so the log ~= log(x) + log(2) * e. */
2245-
result = func(x) + func(2.0) * e;
2246-
}
2247-
else
2248-
/* Successfully converted x to a double. */
2249-
result = func(x);
2250-
return PyFloat_FromDouble(result);
2233+
x = PyLong_AsDouble(arg);
2234+
if (x == -1.0 && PyErr_Occurred()) {
2235+
if (!PyErr_ExceptionMatches(PyExc_OverflowError))
2236+
return NULL;
2237+
/* Here the conversion to double overflowed, but it's possible
2238+
to compute the log anyway. Clear the exception and continue. */
2239+
PyErr_Clear();
2240+
x = _PyLong_Frexp((PyLongObject *)arg, &e);
2241+
if (x == -1.0 && PyErr_Occurred())
2242+
return NULL;
2243+
/* Value is ~= x * 2**e, so the log ~= log(x) + log(2) * e. */
2244+
result = func(x) + func(2.0) * e;
22512245
}
2246+
else
2247+
/* Successfully converted x to a double. */
2248+
result = func(x);
2249+
return PyFloat_FromDouble(result);
2250+
}
22522251

2252+
static PyObject*
2253+
loghelper(PyObject* arg, double (*func)(double))
2254+
{
2255+
/* If it is int, do it ourselves. */
2256+
if (PyLong_Check(arg)) {
2257+
return loghelper_int(arg, func);
2258+
}
22532259
/* Else let libm handle it by itself. */
2254-
return math_1(arg, func, 0);
2260+
PyObject *res = math_1(arg, func, 0);
2261+
if (res == NULL &&
2262+
PyErr_ExceptionMatches(PyExc_OverflowError) &&
2263+
PyIndex_Check(arg))
2264+
{
2265+
/* Here the conversion to double overflowed, but it's possible
2266+
to compute the log anyway. Clear the exception, convert to
2267+
integer and continue. */
2268+
PyErr_Clear();
2269+
arg = _PyNumber_Index(arg);
2270+
if (arg == NULL) {
2271+
return NULL;
2272+
}
2273+
res = loghelper_int(arg, func);
2274+
Py_DECREF(arg);
2275+
}
2276+
return res;
22552277
}
22562278

22572279

0 commit comments

Comments
 (0)