Skip to content

Commit 5990d28

Browse files
committed
Issue python#20539: Improve math.factorial error messages and types for large inputs.
- Better message for the OverflowError in large positive inputs. - Changed exception type from OverflowError to ValueError for large negative inputs.
1 parent 6ed7c20 commit 5990d28

3 files changed

Lines changed: 26 additions & 6 deletions

File tree

Lib/test/test_math.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -422,9 +422,17 @@ def testFactorial(self):
422422
self.assertEqual(math.factorial(i), py_factorial(i))
423423
self.assertRaises(ValueError, math.factorial, -1)
424424
self.assertRaises(ValueError, math.factorial, -1.0)
425+
self.assertRaises(ValueError, math.factorial, -10**100)
426+
self.assertRaises(ValueError, math.factorial, -1e100)
425427
self.assertRaises(ValueError, math.factorial, math.pi)
426-
self.assertRaises(OverflowError, math.factorial, sys.maxsize+1)
427-
self.assertRaises(OverflowError, math.factorial, 10e100)
428+
429+
# Other implementations may place different upper bounds.
430+
@support.cpython_only
431+
def testFactorialHugeInputs(self):
432+
# Currently raises ValueError for inputs that are too large
433+
# to fit into a C long.
434+
self.assertRaises(OverflowError, math.factorial, 10**100)
435+
self.assertRaises(OverflowError, math.factorial, 1e100)
428436

429437
def testFloor(self):
430438
self.assertRaises(TypeError, math.floor)

Misc/NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ Core and Builtins
3131
Library
3232
-------
3333

34+
- Issue #20539: Improved math.factorial error message for large positive inputs
35+
and changed exception type (OverflowError -> ValueError) for large negative
36+
inputs.
37+
3438
- Issue #21172: isinstance check relaxed from dict to collections.Mapping.
3539

3640
- Issue #21155: asyncio.EventLoop.create_unix_server() now raises a ValueError

Modules/mathmodule.c

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1408,6 +1408,7 @@ static PyObject *
14081408
math_factorial(PyObject *self, PyObject *arg)
14091409
{
14101410
long x;
1411+
int overflow;
14111412
PyObject *result, *odd_part, *two_valuation;
14121413

14131414
if (PyFloat_Check(arg)) {
@@ -1421,15 +1422,22 @@ math_factorial(PyObject *self, PyObject *arg)
14211422
lx = PyLong_FromDouble(dx);
14221423
if (lx == NULL)
14231424
return NULL;
1424-
x = PyLong_AsLong(lx);
1425+
x = PyLong_AsLongAndOverflow(lx, &overflow);
14251426
Py_DECREF(lx);
14261427
}
14271428
else
1428-
x = PyLong_AsLong(arg);
1429+
x = PyLong_AsLongAndOverflow(arg, &overflow);
14291430

1430-
if (x == -1 && PyErr_Occurred())
1431+
if (x == -1 && PyErr_Occurred()) {
1432+
return NULL;
1433+
}
1434+
else if (overflow == 1) {
1435+
PyErr_Format(PyExc_OverflowError,
1436+
"factorial() argument should not exceed %ld",
1437+
LONG_MAX);
14311438
return NULL;
1432-
if (x < 0) {
1439+
}
1440+
else if (overflow == -1 || x < 0) {
14331441
PyErr_SetString(PyExc_ValueError,
14341442
"factorial() not defined for negative values");
14351443
return NULL;

0 commit comments

Comments
 (0)