diff --git a/Doc/library/math.rst b/Doc/library/math.rst index ecb1d4102cac31..6cdc4bd801a6a5 100644 --- a/Doc/library/math.rst +++ b/Doc/library/math.rst @@ -842,7 +842,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 9f327cf904da1b..6cdf2e38299069 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -115,6 +115,10 @@ math * Add :func:`math.isnormal` and :func:`math.issubnormal` functions. (Contributed by Sergey B Kirpichev in :gh:`132908`.) +* 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`.) + os.path ------- diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index 384ad5c828d9b3..18c1fc54665f1a 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -2091,15 +2091,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' @@ -2132,15 +2131,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 bbbb49115681de..cbbd8b7aedf897 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);