diff --git a/Doc/library/cmath.rst b/Doc/library/cmath.rst index 26518a0458fd81..0b1c6a866829f2 100644 --- a/Doc/library/cmath.rst +++ b/Doc/library/cmath.rst @@ -38,6 +38,14 @@ the function is then applied to the result of the conversion. 1.4142135623730951j +Most functions compute and return the C99 Annex G recommended result. +If the standard recommends raising ``FE_DIVBYZERO`` or ``FE_INVALID`` +floating-point exceptions, a :exc:`ValueError` is raised and the recommended +result is available as the ``value`` attribute of the exception object. +If a range error occurs due to an overflow in any component of the result, +an :exc:`OverflowError` is raised. + + ==================================================== ============================================ **Conversions to and from polar coordinates** -------------------------------------------------------------------------------------------------- diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index daf3e8fb6c2c2b..418a9672ef2662 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -89,6 +89,14 @@ New modules Improved modules ================ +cmath +----- + +* Provide C99 Annex G return values for :mod:`cmath`'s functions as the + ``value`` attribute of exception objects. + (Contributed by Sergey B Kirpichev in :gh:`133895`.) + + dbm --- diff --git a/Lib/test/test_cmath.py b/Lib/test/test_cmath.py index a96a5780b31b6f..35bac069570e36 100644 --- a/Lib/test/test_cmath.py +++ b/Lib/test/test_cmath.py @@ -318,22 +318,23 @@ def polar_complex(z): if 'divide-by-zero' in flags or 'invalid' in flags: try: actual = function(arg) - except ValueError: - continue + except ValueError as exc: + actual = exc.value else: self.fail('ValueError not raised in test ' '{}: {}(complex({!r}, {!r}))'.format(id, fn, ar, ai)) - if 'overflow' in flags: + elif 'overflow' in flags: try: actual = function(arg) - except OverflowError: - continue + except OverflowError as exc: + actual = exc.value if fn != 'polar' else complex(*exc.value) else: self.fail('OverflowError not raised in test ' '{}: {}(complex({!r}, {!r}))'.format(id, fn, ar, ai)) - actual = function(arg) + else: + actual = function(arg) if 'ignore-real-sign' in flags: actual = complex(abs(actual.real), actual.imag) diff --git a/Misc/NEWS.d/next/Library/2025-06-01-10-38-00.gh-issue-133895.0X7c-V.rst b/Misc/NEWS.d/next/Library/2025-06-01-10-38-00.gh-issue-133895.0X7c-V.rst new file mode 100644 index 00000000000000..d645f6dfd6e6b3 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-06-01-10-38-00.gh-issue-133895.0X7c-V.rst @@ -0,0 +1,2 @@ +Provide C99 Annex G return values for :mod:`cmath`'s functions as the +``value`` attribute of exception objects. Patch by Sergey B Kirpichev. diff --git a/Modules/clinic/cmathmodule.c.h b/Modules/clinic/cmathmodule.c.h index 7f9e65baf120ea..54b65943b484a4 100644 --- a/Modules/clinic/cmathmodule.c.h +++ b/Modules/clinic/cmathmodule.c.h @@ -34,17 +34,14 @@ cmath_acos(PyObject *module, PyObject *arg) /* modifications for z */ errno = 0; _return_value = cmath_acos_impl(module, z); - if (errno == EDOM) { - PyErr_SetString(PyExc_ValueError, "math domain error"); - goto exit; - } - else if (errno == ERANGE) { - PyErr_SetString(PyExc_OverflowError, "math range error"); + return_value = PyComplex_FromCComplex(_return_value); + if (errno) { + if (return_value) { + set_cmath_error(return_value); + } + return_value = NULL; goto exit; } - else { - return_value = PyComplex_FromCComplex(_return_value); - } exit: return return_value; @@ -76,17 +73,14 @@ cmath_acosh(PyObject *module, PyObject *arg) /* modifications for z */ errno = 0; _return_value = cmath_acosh_impl(module, z); - if (errno == EDOM) { - PyErr_SetString(PyExc_ValueError, "math domain error"); - goto exit; - } - else if (errno == ERANGE) { - PyErr_SetString(PyExc_OverflowError, "math range error"); + return_value = PyComplex_FromCComplex(_return_value); + if (errno) { + if (return_value) { + set_cmath_error(return_value); + } + return_value = NULL; goto exit; } - else { - return_value = PyComplex_FromCComplex(_return_value); - } exit: return return_value; @@ -118,17 +112,14 @@ cmath_asin(PyObject *module, PyObject *arg) /* modifications for z */ errno = 0; _return_value = cmath_asin_impl(module, z); - if (errno == EDOM) { - PyErr_SetString(PyExc_ValueError, "math domain error"); - goto exit; - } - else if (errno == ERANGE) { - PyErr_SetString(PyExc_OverflowError, "math range error"); + return_value = PyComplex_FromCComplex(_return_value); + if (errno) { + if (return_value) { + set_cmath_error(return_value); + } + return_value = NULL; goto exit; } - else { - return_value = PyComplex_FromCComplex(_return_value); - } exit: return return_value; @@ -160,17 +151,14 @@ cmath_asinh(PyObject *module, PyObject *arg) /* modifications for z */ errno = 0; _return_value = cmath_asinh_impl(module, z); - if (errno == EDOM) { - PyErr_SetString(PyExc_ValueError, "math domain error"); - goto exit; - } - else if (errno == ERANGE) { - PyErr_SetString(PyExc_OverflowError, "math range error"); + return_value = PyComplex_FromCComplex(_return_value); + if (errno) { + if (return_value) { + set_cmath_error(return_value); + } + return_value = NULL; goto exit; } - else { - return_value = PyComplex_FromCComplex(_return_value); - } exit: return return_value; @@ -202,17 +190,14 @@ cmath_atan(PyObject *module, PyObject *arg) /* modifications for z */ errno = 0; _return_value = cmath_atan_impl(module, z); - if (errno == EDOM) { - PyErr_SetString(PyExc_ValueError, "math domain error"); - goto exit; - } - else if (errno == ERANGE) { - PyErr_SetString(PyExc_OverflowError, "math range error"); + return_value = PyComplex_FromCComplex(_return_value); + if (errno) { + if (return_value) { + set_cmath_error(return_value); + } + return_value = NULL; goto exit; } - else { - return_value = PyComplex_FromCComplex(_return_value); - } exit: return return_value; @@ -244,17 +229,14 @@ cmath_atanh(PyObject *module, PyObject *arg) /* modifications for z */ errno = 0; _return_value = cmath_atanh_impl(module, z); - if (errno == EDOM) { - PyErr_SetString(PyExc_ValueError, "math domain error"); - goto exit; - } - else if (errno == ERANGE) { - PyErr_SetString(PyExc_OverflowError, "math range error"); + return_value = PyComplex_FromCComplex(_return_value); + if (errno) { + if (return_value) { + set_cmath_error(return_value); + } + return_value = NULL; goto exit; } - else { - return_value = PyComplex_FromCComplex(_return_value); - } exit: return return_value; @@ -286,17 +268,14 @@ cmath_cos(PyObject *module, PyObject *arg) /* modifications for z */ errno = 0; _return_value = cmath_cos_impl(module, z); - if (errno == EDOM) { - PyErr_SetString(PyExc_ValueError, "math domain error"); - goto exit; - } - else if (errno == ERANGE) { - PyErr_SetString(PyExc_OverflowError, "math range error"); + return_value = PyComplex_FromCComplex(_return_value); + if (errno) { + if (return_value) { + set_cmath_error(return_value); + } + return_value = NULL; goto exit; } - else { - return_value = PyComplex_FromCComplex(_return_value); - } exit: return return_value; @@ -328,17 +307,14 @@ cmath_cosh(PyObject *module, PyObject *arg) /* modifications for z */ errno = 0; _return_value = cmath_cosh_impl(module, z); - if (errno == EDOM) { - PyErr_SetString(PyExc_ValueError, "math domain error"); - goto exit; - } - else if (errno == ERANGE) { - PyErr_SetString(PyExc_OverflowError, "math range error"); + return_value = PyComplex_FromCComplex(_return_value); + if (errno) { + if (return_value) { + set_cmath_error(return_value); + } + return_value = NULL; goto exit; } - else { - return_value = PyComplex_FromCComplex(_return_value); - } exit: return return_value; @@ -370,17 +346,14 @@ cmath_exp(PyObject *module, PyObject *arg) /* modifications for z */ errno = 0; _return_value = cmath_exp_impl(module, z); - if (errno == EDOM) { - PyErr_SetString(PyExc_ValueError, "math domain error"); - goto exit; - } - else if (errno == ERANGE) { - PyErr_SetString(PyExc_OverflowError, "math range error"); + return_value = PyComplex_FromCComplex(_return_value); + if (errno) { + if (return_value) { + set_cmath_error(return_value); + } + return_value = NULL; goto exit; } - else { - return_value = PyComplex_FromCComplex(_return_value); - } exit: return return_value; @@ -412,17 +385,14 @@ cmath_log10(PyObject *module, PyObject *arg) /* modifications for z */ errno = 0; _return_value = cmath_log10_impl(module, z); - if (errno == EDOM) { - PyErr_SetString(PyExc_ValueError, "math domain error"); - goto exit; - } - else if (errno == ERANGE) { - PyErr_SetString(PyExc_OverflowError, "math range error"); + return_value = PyComplex_FromCComplex(_return_value); + if (errno) { + if (return_value) { + set_cmath_error(return_value); + } + return_value = NULL; goto exit; } - else { - return_value = PyComplex_FromCComplex(_return_value); - } exit: return return_value; @@ -454,17 +424,14 @@ cmath_sin(PyObject *module, PyObject *arg) /* modifications for z */ errno = 0; _return_value = cmath_sin_impl(module, z); - if (errno == EDOM) { - PyErr_SetString(PyExc_ValueError, "math domain error"); - goto exit; - } - else if (errno == ERANGE) { - PyErr_SetString(PyExc_OverflowError, "math range error"); + return_value = PyComplex_FromCComplex(_return_value); + if (errno) { + if (return_value) { + set_cmath_error(return_value); + } + return_value = NULL; goto exit; } - else { - return_value = PyComplex_FromCComplex(_return_value); - } exit: return return_value; @@ -496,17 +463,14 @@ cmath_sinh(PyObject *module, PyObject *arg) /* modifications for z */ errno = 0; _return_value = cmath_sinh_impl(module, z); - if (errno == EDOM) { - PyErr_SetString(PyExc_ValueError, "math domain error"); - goto exit; - } - else if (errno == ERANGE) { - PyErr_SetString(PyExc_OverflowError, "math range error"); + return_value = PyComplex_FromCComplex(_return_value); + if (errno) { + if (return_value) { + set_cmath_error(return_value); + } + return_value = NULL; goto exit; } - else { - return_value = PyComplex_FromCComplex(_return_value); - } exit: return return_value; @@ -538,17 +502,14 @@ cmath_sqrt(PyObject *module, PyObject *arg) /* modifications for z */ errno = 0; _return_value = cmath_sqrt_impl(module, z); - if (errno == EDOM) { - PyErr_SetString(PyExc_ValueError, "math domain error"); - goto exit; - } - else if (errno == ERANGE) { - PyErr_SetString(PyExc_OverflowError, "math range error"); + return_value = PyComplex_FromCComplex(_return_value); + if (errno) { + if (return_value) { + set_cmath_error(return_value); + } + return_value = NULL; goto exit; } - else { - return_value = PyComplex_FromCComplex(_return_value); - } exit: return return_value; @@ -580,17 +541,14 @@ cmath_tan(PyObject *module, PyObject *arg) /* modifications for z */ errno = 0; _return_value = cmath_tan_impl(module, z); - if (errno == EDOM) { - PyErr_SetString(PyExc_ValueError, "math domain error"); - goto exit; - } - else if (errno == ERANGE) { - PyErr_SetString(PyExc_OverflowError, "math range error"); + return_value = PyComplex_FromCComplex(_return_value); + if (errno) { + if (return_value) { + set_cmath_error(return_value); + } + return_value = NULL; goto exit; } - else { - return_value = PyComplex_FromCComplex(_return_value); - } exit: return return_value; @@ -622,17 +580,14 @@ cmath_tanh(PyObject *module, PyObject *arg) /* modifications for z */ errno = 0; _return_value = cmath_tanh_impl(module, z); - if (errno == EDOM) { - PyErr_SetString(PyExc_ValueError, "math domain error"); - goto exit; - } - else if (errno == ERANGE) { - PyErr_SetString(PyExc_OverflowError, "math range error"); + return_value = PyComplex_FromCComplex(_return_value); + if (errno) { + if (return_value) { + set_cmath_error(return_value); + } + return_value = NULL; goto exit; } - else { - return_value = PyComplex_FromCComplex(_return_value); - } exit: return return_value; @@ -744,7 +699,7 @@ PyDoc_STRVAR(cmath_rect__doc__, #define CMATH_RECT_METHODDEF \ {"rect", _PyCFunction_CAST(cmath_rect), METH_FASTCALL, cmath_rect__doc__}, -static PyObject * +static Py_complex cmath_rect_impl(PyObject *module, double r, double phi); static PyObject * @@ -753,6 +708,7 @@ cmath_rect(PyObject *module, PyObject *const *args, Py_ssize_t nargs) PyObject *return_value = NULL; double r; double phi; + Py_complex _return_value; if (!_PyArg_CheckPositional("rect", nargs, 2, 2)) { goto exit; @@ -777,7 +733,15 @@ cmath_rect(PyObject *module, PyObject *const *args, Py_ssize_t nargs) goto exit; } } - return_value = cmath_rect_impl(module, r, phi); + _return_value = cmath_rect_impl(module, r, phi); + return_value = PyComplex_FromCComplex(_return_value); + if (errno) { + if (return_value) { + set_cmath_error(return_value); + } + return_value = NULL; + goto exit; + } exit: return return_value; @@ -985,4 +949,4 @@ cmath_isclose(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec exit: return return_value; } -/*[clinic end generated code: output=631db17fb1c79d66 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=1f9be4ea0ab6951b input=a9049054013a1b77]*/ diff --git a/Modules/cmathmodule.c b/Modules/cmathmodule.c index 81cbf0d554de3c..f86017b18cbec3 100644 --- a/Modules/cmathmodule.c +++ b/Modules/cmathmodule.c @@ -16,6 +16,48 @@ /* For _Py_log1p with workarounds for buggy handling of zeros. */ #include "_math.h" +/* A little helper to set 'value' attribute on exceptions. + Warning: steal a reference to value. */ + +static void +set_cmath_error(PyObject *value) +{ + if (errno == EDOM || errno == ERANGE) { + PyObject *exc_type; + PyObject *exc_string; + + if (errno == EDOM) { + exc_type = PyExc_ValueError; + exc_string = PyUnicode_FromString("math domain error"); + } + else { + exc_type = PyExc_OverflowError; + exc_string = PyUnicode_FromString("math range error"); + } + if (!exc_string) { + Py_DECREF(value); + return; + } + PyObject *exc = PyObject_CallOneArg(exc_type, exc_string); + + Py_DECREF(exc_string); + if (!exc) { + Py_DECREF(value); + return; + } + if (PyObject_SetAttrString(exc, "value", value)) { + Py_DECREF(value); + return; + } + Py_DECREF(value); + PyErr_SetRaisedException(exc); + } + else { /* Unexpected math error */ + Py_DECREF(value); + PyErr_SetFromErrno(PyExc_ValueError); + } +} + #include "clinic/cmathmodule.c.h" /*[clinic input] module cmath @@ -34,20 +76,17 @@ class Py_complex_protected_return_converter(CReturnConverter): def render(self, function, data): self.declare(data) data.return_conversion.append(""" -if (errno == EDOM) { - PyErr_SetString(PyExc_ValueError, "math domain error"); - goto exit; -} -else if (errno == ERANGE) { - PyErr_SetString(PyExc_OverflowError, "math range error"); +return_value = PyComplex_FromCComplex(_return_value); +if (errno) { + if (return_value) { + set_cmath_error(return_value); + } + return_value = NULL; goto exit; } -else { - return_value = PyComplex_FromCComplex(_return_value); -} """.strip()) [python start generated code]*/ -/*[python end generated code: output=da39a3ee5e6b4b0d input=8b27adb674c08321]*/ +/*[python end generated code: output=da39a3ee5e6b4b0d input=fff1087cce9b4c85]*/ #if (FLT_RADIX != 2 && FLT_RADIX != 16) #error "Modules/cmathmodule.c expects FLT_RADIX to be 2 or 16" @@ -438,6 +477,9 @@ cmath_cosh_impl(PyObject *module, Py_complex z) x_minus_one = z.real - copysign(1., z.real); r.real = cos(z.imag) * cosh(x_minus_one) * Py_MATH_E; r.imag = sin(z.imag) * sinh(x_minus_one) * Py_MATH_E; + if (isnan(r.imag)) { + r.imag = copysign(0., z.real*z.imag); + } } else { r.real = cos(z.imag) * cosh(z.real); r.imag = sin(z.imag) * sinh(z.real); @@ -674,6 +716,9 @@ cmath_sinh_impl(PyObject *module, Py_complex z) x_minus_one = z.real - copysign(1., z.real); r.real = cos(z.imag) * sinh(x_minus_one) * Py_MATH_E; r.imag = sin(z.imag) * cosh(x_minus_one) * Py_MATH_E; + if (isnan(r.imag)) { + r.imag = copysign(0., z.imag); + } } else { r.real = cos(z.imag) * sinh(z.real); r.imag = sin(z.imag) * cosh(z.real); @@ -888,9 +933,16 @@ cmath_log_impl(PyObject *module, Py_complex x, PyObject *y_obj) y = c_log(y); x = _Py_c_quot(x, y); } - if (errno != 0) - return math_error(); - return PyComplex_FromCComplex(x); + + PyObject *res = PyComplex_FromCComplex(x); + + if (errno) { + if (res) { + set_cmath_error(res); + } + return NULL; + } + return res; } @@ -952,10 +1004,16 @@ cmath_polar_impl(PyObject *module, Py_complex z) errno = 0; phi = atan2(z.imag, z.real); /* should not cause any exception */ r = _Py_c_abs(z); /* sets errno to ERANGE on overflow */ - if (errno != 0) - return math_error(); - else - return Py_BuildValue("dd", r, phi); + + PyObject *res = Py_BuildValue("dd", r, phi); + + if (errno != 0) { + if (res) { + set_cmath_error(res); + } + return NULL; + } + return res; } /* @@ -972,7 +1030,7 @@ cmath_polar_impl(PyObject *module, Py_complex z) static Py_complex rect_special_values[7][7]; /*[clinic input] -cmath.rect +cmath.rect -> Py_complex_protected r: double phi: double @@ -981,9 +1039,9 @@ cmath.rect Convert from polar coordinates to rectangular coordinates. [clinic start generated code]*/ -static PyObject * +static Py_complex cmath_rect_impl(PyObject *module, double r, double phi) -/*[clinic end generated code: output=385a0690925df2d5 input=24c5646d147efd69]*/ +/*[clinic end generated code: output=74ff3d17585f3388 input=50e60c5d28c834e6]*/ { Py_complex z; errno = 0; @@ -1027,11 +1085,7 @@ cmath_rect_impl(PyObject *module, double r, double phi) z.imag = r * sin(phi); errno = 0; } - - if (errno != 0) - return math_error(); - else - return PyComplex_FromCComplex(z); + return z; } /*[clinic input] pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy