From b485e50fde3be08d796a2dac66cb822da1226bb3 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Wed, 17 Sep 2025 16:50:15 -0500 Subject: [PATCH] gh-139074: Fix missing high precision case in sumprod() (gh-139075) --- Lib/test/test_math.py | 4 ++- ...-09-17-13-21-26.gh-issue-139074.dVZO5F.rst | 2 ++ Modules/mathmodule.c | 33 +++++++++---------- 3 files changed, 21 insertions(+), 18 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-09-17-13-21-26.gh-issue-139074.dVZO5F.rst diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index e3b0d4fa9ee..92326a46c33 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -1485,7 +1485,9 @@ def __rmul__(self, other): # Error cases that arose during development args = ((-5, -5, 10), (1.5, 4611686018427387904, 2305843009213693952)) - self.assertEqual(sumprod(*args), 0.0) + self.assertEqual(sumprod(*args), -7.5) + self.assertEqual(sumprod([-0.01, 1, -1, 0.01], [1, 1, 1, 1]), 0.0) + self.assertEqual(sumprod([1, 1, 1, 1], [-0.01, 1, -1, 0.01], ), 0.0) @requires_IEEE_754 @unittest.skipIf(HAVE_DOUBLE_ROUNDING, diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-09-17-13-21-26.gh-issue-139074.dVZO5F.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-09-17-13-21-26.gh-issue-139074.dVZO5F.rst new file mode 100644 index 00000000000..56c9f21296d --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-09-17-13-21-26.gh-issue-139074.dVZO5F.rst @@ -0,0 +1,2 @@ +Fixed a missing case in :func:`math.sumprod` where a low precision path was +taken when an int/int input pair followed a float input. diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index c631beb9ce5..d8bf9e76cdd 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -2937,24 +2937,10 @@ math_sumprod_impl(PyObject *module, PyObject *p, PyObject *q) if (!finished) { double flt_p, flt_q; - bool p_type_float = PyFloat_CheckExact(p_i); - bool q_type_float = PyFloat_CheckExact(q_i); - if (p_type_float && q_type_float) { + + if (PyFloat_CheckExact(p_i)) { flt_p = PyFloat_AS_DOUBLE(p_i); - flt_q = PyFloat_AS_DOUBLE(q_i); - } else if (p_type_float && (PyLong_CheckExact(q_i) || PyBool_Check(q_i))) { - /* We care about float/int pairs and int/float pairs because - they arise naturally in several use cases such as price - times quantity, measurements with integer weights, or - data selected by a vector of bools. */ - flt_p = PyFloat_AS_DOUBLE(p_i); - flt_q = PyLong_AsDouble(q_i); - if (flt_q == -1.0 && PyErr_Occurred()) { - PyErr_Clear(); - goto finalize_flt_path; - } - } else if (q_type_float && (PyLong_CheckExact(p_i) || PyBool_Check(p_i))) { - flt_q = PyFloat_AS_DOUBLE(q_i); + } else if (PyLong_CheckExact(p_i) || PyBool_Check(p_i)) { flt_p = PyLong_AsDouble(p_i); if (flt_p == -1.0 && PyErr_Occurred()) { PyErr_Clear(); @@ -2963,6 +2949,19 @@ math_sumprod_impl(PyObject *module, PyObject *p, PyObject *q) } else { goto finalize_flt_path; } + + if (PyFloat_CheckExact(q_i)) { + flt_q = PyFloat_AS_DOUBLE(q_i); + } else if (PyLong_CheckExact(q_i) || PyBool_Check(q_i)) { + flt_q = PyLong_AsDouble(q_i); + if (flt_q == -1.0 && PyErr_Occurred()) { + PyErr_Clear(); + goto finalize_flt_path; + } + } else { + goto finalize_flt_path; + } + TripleLength new_flt_total = tl_fma(flt_p, flt_q, flt_total); if (isfinite(new_flt_total.hi)) { flt_total = new_flt_total;