mirror of
https://github.com/python/cpython.git
synced 2025-12-08 06:10:17 +00:00
gh-120950: Fix overflow in math.log() with large int-like argument (GH-121011)
Handling of arbitrary large int-like argument is now consistent with handling arbitrary large int arguments.
This commit is contained in:
parent
9e7340cd3b
commit
4359706ac8
3 changed files with 111 additions and 30 deletions
|
|
@ -57,6 +57,7 @@ raised for division by zero and mod by zero.
|
|||
#endif
|
||||
|
||||
#include "Python.h"
|
||||
#include "pycore_abstract.h" // _PyNumber_Index()
|
||||
#include "pycore_bitutils.h" // _Py_bit_length()
|
||||
#include "pycore_call.h" // _PyObject_CallNoArgs()
|
||||
#include "pycore_import.h" // _PyImport_SetModuleString()
|
||||
|
|
@ -1577,44 +1578,63 @@ math_modf_impl(PyObject *module, double x)
|
|||
However, intermediate overflow is possible for an int if the number of bits
|
||||
in that int is larger than PY_SSIZE_T_MAX. */
|
||||
|
||||
static PyObject*
|
||||
loghelper_int(PyObject* arg, double (*func)(double))
|
||||
{
|
||||
/* If it is int, do it ourselves. */
|
||||
double x, result;
|
||||
int64_t e;
|
||||
|
||||
/* Negative or zero inputs give a ValueError. */
|
||||
if (!_PyLong_IsPositive((PyLongObject *)arg)) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"expected a positive input");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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);
|
||||
assert(!PyErr_Occurred());
|
||||
/* Value is ~= x * 2**e, so the log ~= log(x) + log(2) * e. */
|
||||
result = fma(func(2.0), (double)e, func(x));
|
||||
}
|
||||
else
|
||||
/* Successfully converted x to a double. */
|
||||
result = func(x);
|
||||
return PyFloat_FromDouble(result);
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
loghelper(PyObject* arg, double (*func)(double))
|
||||
{
|
||||
/* If it is int, do it ourselves. */
|
||||
if (PyLong_Check(arg)) {
|
||||
double x, result;
|
||||
int64_t e;
|
||||
|
||||
/* Negative or zero inputs give a ValueError. */
|
||||
if (!_PyLong_IsPositive((PyLongObject *)arg)) {
|
||||
/* The input can be an arbitrary large integer, so we
|
||||
don't include it's value in the error message. */
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"expected a positive input");
|
||||
return loghelper_int(arg, func);
|
||||
}
|
||||
/* Else let libm handle it by itself. */
|
||||
PyObject *res = math_1(arg, func, 0, "expected a positive input, got %s");
|
||||
if (res == NULL &&
|
||||
PyErr_ExceptionMatches(PyExc_OverflowError) &&
|
||||
PyIndex_Check(arg))
|
||||
{
|
||||
/* Here the conversion to double overflowed, but it's possible
|
||||
to compute the log anyway. Clear the exception, convert to
|
||||
integer and continue. */
|
||||
PyErr_Clear();
|
||||
arg = _PyNumber_Index(arg);
|
||||
if (arg == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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);
|
||||
assert(e >= 0);
|
||||
assert(!PyErr_Occurred());
|
||||
/* Value is ~= x * 2**e, so the log ~= log(x) + log(2) * e. */
|
||||
result = fma(func(2.0), (double)e, func(x));
|
||||
}
|
||||
else
|
||||
/* Successfully converted x to a double. */
|
||||
result = func(x);
|
||||
return PyFloat_FromDouble(result);
|
||||
res = loghelper_int(arg, func);
|
||||
Py_DECREF(arg);
|
||||
}
|
||||
|
||||
/* Else let libm handle it by itself. */
|
||||
return math_1(arg, func, 0, "expected a positive input, got %s");
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue