Skip to content

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

Open
@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