diff --git a/Doc/library/math.rst b/Doc/library/math.rst index 11d3b756e21322..f371cd03f77856 100644 --- a/Doc/library/math.rst +++ b/Doc/library/math.rst @@ -822,7 +822,10 @@ Constants :exc:`ValueError` for invalid operations like ``sqrt(-1.0)`` or ``log(0.0)`` (where C99 Annex F recommends signaling invalid operation or divide-by-zero), and :exc:`OverflowError` for results that overflow (for example, - ``exp(1000.0)``). A NaN will not be returned from any of the functions + ``exp(1000.0)``). The Annex F recommended result is available as + the ``value`` property of the :exc:`ValueError` exception. + + A NaN will not be returned from any of the functions above unless one or more of the input arguments was a NaN; in that case, most functions will return a NaN, but (again following C99 Annex F) there are some exceptions to this rule, for example ``pow(float('nan'), 0.0)`` or diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 244ce327763f57..f726ae3df731fe 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -105,6 +105,14 @@ difflib (Contributed by Jiahao Li in :gh:`134580`.) +math +---- + +* Provide C99 Annex F return values for :mod:`math`'s functions as the + ``value`` attribute of the :exc:`ValueError` exception object. + (Contributed by Sergey B Kirpichev in :gh:`133895`.) + + shelve ------ diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index d14336f8bac498..9d2fa47f2418d7 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -2069,15 +2069,14 @@ def test_testfile(self): func = getattr(math, fn) - if 'invalid' in flags or 'divide-by-zero' in flags: - er = 'ValueError' - elif 'overflow' in flags: + if 'overflow' in flags: er = 'OverflowError' try: result = func(ar) - except ValueError: - result = 'ValueError' + except ValueError as exc: + self.assertTrue('invalid' in flags or 'divide-by-zero' in flags) + result = exc.value except OverflowError: result = 'OverflowError' @@ -2110,15 +2109,14 @@ def test_mtestfile(self): for id, fn, arg, expected, flags in parse_mtestfile(math_testcases): func = getattr(math, fn) - if 'invalid' in flags or 'divide-by-zero' in flags: - expected = 'ValueError' - elif 'overflow' in flags: + if 'overflow' in flags: expected = 'OverflowError' try: got = func(arg) - except ValueError: - got = 'ValueError' + except ValueError as exc: + got = exc.value + self.assertTrue('invalid' in flags or 'divide-by-zero' in flags) except OverflowError: got = 'OverflowError' diff --git a/Misc/NEWS.d/next/Library/2025-06-01-16-10-07.gh-issue-133895.1-SE2w.rst b/Misc/NEWS.d/next/Library/2025-06-01-16-10-07.gh-issue-133895.1-SE2w.rst new file mode 100644 index 00000000000000..c0657d650c6705 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-06-01-16-10-07.gh-issue-133895.1-SE2w.rst @@ -0,0 +1,2 @@ +Provide C99 Annex F return values for :mod:`math`'s functions as the ``value`` +attribute of the :exc:`ValueError` exception object. Patch by Sergey B Kirpichev. diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 71d9c1387f5780..8eddb6046e57b3 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -840,6 +840,20 @@ math_lcm_impl(PyObject *module, PyObject * const *args, } +static void +set_exc_value(double x) +{ + PyObject *exc = PyErr_GetRaisedException(); + PyObject *value = PyFloat_FromDouble(x); + + if (value) { + PyObject_SetAttrString(exc, "value", value); + } + Py_XDECREF(value); + PyErr_SetRaisedException(exc); +} + + /* Call is_error when errno != 0, and where x is the result libm * returned. is_error will usually set up an exception and return * true (1), but may return false (0) without setting up an exception. @@ -852,6 +866,7 @@ is_error(double x, int raise_edom) if (errno == EDOM) { if (raise_edom) { PyErr_SetString(PyExc_ValueError, "math domain error"); + set_exc_value(x); } } @@ -954,6 +969,7 @@ math_1(PyObject *arg, double (*func) (double), int can_overflow, else { PyErr_SetString(PyExc_ValueError, "math domain error"); } + set_exc_value(r); return NULL; } @@ -979,6 +995,7 @@ math_1a(PyObject *arg, double (*func) (double), const char *err_msg) PyMem_Free(buf); } } + set_exc_value(r); return NULL; } return PyFloat_FromDouble(r);