Skip to content

Pass return value on ValueError exceptions in the cmath/math modules #133895

@skirpichev

Description

@skirpichev

Feature or enhancement

Proposal:

Currently the error handling happens this way for the cmath module (taken from module comments):

Each of the c_* functions computes and returns the C99 Annex G recommended result and also sets errno as follows: errno = 0 if no floating-point exception is associated with the result; errno = EDOM if C99 Annex G recommends raising divide-by-zero or invalid for this result; and errno = ERANGE where the overflow floating-point signal should be raised.

The ValueError raised for EDOM and the OverflowError - for ERANGE, but the Annex G result is hidden from the pure-Python world. Though, it might be helpful for applications. E.g. clog(-0+0i) returns -∞+πi and clog(+0+0i) returns -∞+0i - correct one-sided limits in the pole of log(). (BTW, something like PoleError could be better here, but that's another story.)

The mpmath and the gmpy2 (per default, if trap_divzero and/or trap_invalid context options aren't enabled) rather return special values per the C standard, not raise exceptions. And the mpmath also uses builtin float's and math/cmath functions for the fp context. Thus, to override current behavior of the stdlib - we need to catch ValueError from the called function and then process function arguments to return special values, i.e. essentially re-implement handling of special values. But they already are computed in cmath/math functions, so why not return this information with an exception? An example:

>>> import cmath
>>> try:
...     cmath.atanh(1)
... except ValueError as ex:
...     print(ex.value)
...     
(inf+0j)
Initial patch, working for most functions, not using math_error().
diff --git a/Modules/cmathmodule.c b/Modules/cmathmodule.c
index 81cbf0d554..d388241f94 100644
--- a/Modules/cmathmodule.c
+++ b/Modules/cmathmodule.c
@@ -36,6 +36,15 @@ class Py_complex_protected_return_converter(CReturnConverter):
         data.return_conversion.append("""
 if (errno == EDOM) {
     PyErr_SetString(PyExc_ValueError, "math domain error");
+
+    PyObject *exc = PyErr_GetRaisedException();
+    PyObject *value = PyComplex_FromCComplex(_return_value);
+
+    if (value) {
+        PyObject_SetAttrString(exc, "value", value);
+    }
+    Py_DECREF(value);
+    PyErr_SetRaisedException(exc);
     goto exit;
 }
 else if (errno == ERANGE) {

Similar happens in the math module and fix looks simple as well. I didn't check all cases, but it seems that most functions in the cmath/math modules actually compute correct (per C standard and Annex G) answers for special values.

Alternative approach: some global flag to turn on the gmpy2-like behavior (i.e. raise no exceptions, but return special values instead).

Has this already been discussed elsewhere?

This is a minor feature, which does not need previous discussion elsewhere

Links to previous discussion of this feature:

No response

Linked PRs

Metadata

Metadata

Assignees

No one assigned

    Labels

    extension-modulesC modules in the Modules dirtype-featureA feature request or enhancement

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      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