Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions Lib/test/test_math_errors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import unittest
import math
import cmath

class TestMathErrors(unittest.TestCase):
def test_math_value_error_with_value(self):
# Test math.sqrt with negative number
try:
math.sqrt(-1)
except ValueError as e:
self.assertTrue(hasattr(e, 'value'))
self.assertTrue(math.isnan(e.value))

# Test math.log with negative number
try:
math.log(-1)
except ValueError as e:
self.assertTrue(hasattr(e, 'value'))
self.assertTrue(math.isnan(e.value))

# Test math.acos with value > 1
try:
math.acos(2)
except ValueError as e:
self.assertTrue(hasattr(e, 'value'))
self.assertTrue(math.isnan(e.value))

def test_cmath_value_error_with_value(self):
# Test cmath.sqrt with negative number
try:
cmath.sqrt(-1)
except ValueError as e:
self.assertTrue(hasattr(e, 'value'))
self.assertTrue(isinstance(e.value, complex))
self.assertTrue(cmath.isnan(e.value))

# Test cmath.log with negative number
try:
cmath.log(-1)
except ValueError as e:
self.assertTrue(hasattr(e, 'value'))
self.assertTrue(isinstance(e.value, complex))
self.assertTrue(cmath.isnan(e.value))

# Test cmath.acos with value > 1
try:
cmath.acos(2)
except ValueError as e:
self.assertTrue(hasattr(e, 'value'))
self.assertTrue(isinstance(e.value, complex))
self.assertTrue(cmath.isnan(e.value))

def test_math_overflow_error_with_value(self):
# Test math.exp with very large number
try:
math.exp(1000)
except OverflowError as e:
self.assertTrue(hasattr(e, 'value'))
self.assertTrue(math.isinf(e.value))

def test_cmath_overflow_error_with_value(self):
# Test cmath.exp with very large number
try:
cmath.exp(1000)
except OverflowError as e:
self.assertTrue(hasattr(e, 'value'))
self.assertTrue(isinstance(e.value, complex))
self.assertTrue(cmath.isinf(e.value.real) or cmath.isinf(e.value.imag))

if __name__ == '__main__':
unittest.main()
72 changes: 64 additions & 8 deletions Modules/cmathmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,23 @@
self.declare(data)
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) {
PyErr_SetString(PyExc_OverflowError, "math range 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 {
Expand Down Expand Up @@ -408,8 +420,8 @@

/* special treatment for cosh(+/-inf + iy) if y is not a NaN */
if (!isfinite(z.real) || !isfinite(z.imag)) {
if (isinf(z.real) && isfinite(z.imag) &&
(z.imag != 0.)) {
if (isinf(z.real) && isfinite(z.imag)
&& (z.imag != 0.)) {
if (z.real > 0) {
r.real = copysign(INF, cos(z.imag));
r.imag = copysign(INF, sin(z.imag));
Expand Down Expand Up @@ -899,10 +911,24 @@
static PyObject *
math_error(void)
{
if (errno == EDOM)
PyErr_SetString(PyExc_ValueError, "math domain error");
else if (errno == ERANGE)
PyErr_SetString(PyExc_OverflowError, "math range error");
if (errno == EDOM) {
PyObject *exc = PyErr_GetRaisedException();
PyObject *value = PyComplex_FromCComplex(_return_value);

Check failure on line 916 in Modules/cmathmodule.c

View workflow job for this annotation

GitHub Actions / Cross build Linux

‘_return_value’ undeclared (first use in this function)

Check failure on line 916 in Modules/cmathmodule.c

View workflow job for this annotation

GitHub Actions / Windows (free-threading) / Build and test (arm64)

'_return_value': undeclared identifier [C:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check failure on line 916 in Modules/cmathmodule.c

View workflow job for this annotation

GitHub Actions / Windows (free-threading) / Build and test (arm64)

'function': cannot convert from 'int' to 'Py_complex' [C:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check warning on line 916 in Modules/cmathmodule.c

View workflow job for this annotation

GitHub Actions / Windows (free-threading) / Build and test (arm64)

'PyComplex_FromCComplex': different types for formal and actual parameter 1 [C:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check failure on line 916 in Modules/cmathmodule.c

View workflow job for this annotation

GitHub Actions / Windows / Build and test (x64)

'_return_value': undeclared identifier [D:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check failure on line 916 in Modules/cmathmodule.c

View workflow job for this annotation

GitHub Actions / Windows / Build and test (x64)

'function': cannot convert from 'int' to 'Py_complex' [D:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check warning on line 916 in Modules/cmathmodule.c

View workflow job for this annotation

GitHub Actions / Windows / Build and test (x64)

'PyComplex_FromCComplex': different types for formal and actual parameter 1 [D:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check failure on line 916 in Modules/cmathmodule.c

View workflow job for this annotation

GitHub Actions / Windows (free-threading) / Build and test (x64)

'_return_value': undeclared identifier [D:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check failure on line 916 in Modules/cmathmodule.c

View workflow job for this annotation

GitHub Actions / Windows (free-threading) / Build and test (x64)

'function': cannot convert from 'int' to 'Py_complex' [D:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check warning on line 916 in Modules/cmathmodule.c

View workflow job for this annotation

GitHub Actions / Windows (free-threading) / Build and test (x64)

'PyComplex_FromCComplex': different types for formal and actual parameter 1 [D:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check failure on line 916 in Modules/cmathmodule.c

View workflow job for this annotation

GitHub Actions / Hypothesis tests on Ubuntu

‘_return_value’ undeclared (first use in this function)

Check failure on line 916 in Modules/cmathmodule.c

View workflow job for this annotation

GitHub Actions / Windows / Build and test (arm64)

'_return_value': undeclared identifier [C:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check failure on line 916 in Modules/cmathmodule.c

View workflow job for this annotation

GitHub Actions / Windows / Build and test (arm64)

'function': cannot convert from 'int' to 'Py_complex' [C:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check warning on line 916 in Modules/cmathmodule.c

View workflow job for this annotation

GitHub Actions / Windows / Build and test (arm64)

'PyComplex_FromCComplex': different types for formal and actual parameter 1 [C:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check failure on line 916 in Modules/cmathmodule.c

View workflow job for this annotation

GitHub Actions / Ubuntu (free-threading) / build and test (ubuntu-24.04)

‘_return_value’ undeclared (first use in this function)

Check failure on line 916 in Modules/cmathmodule.c

View workflow job for this annotation

GitHub Actions / Ubuntu / build and test (ubuntu-24.04)

‘_return_value’ undeclared (first use in this function)

Check failure on line 916 in Modules/cmathmodule.c

View workflow job for this annotation

GitHub Actions / Ubuntu (bolt) / build and test (ubuntu-24.04)

‘_return_value’ undeclared (first use in this function)

Check failure on line 916 in Modules/cmathmodule.c

View workflow job for this annotation

GitHub Actions / Ubuntu / build and test (ubuntu-24.04-arm)

‘_return_value’ undeclared (first use in this function)

Check failure on line 916 in Modules/cmathmodule.c

View workflow job for this annotation

GitHub Actions / Address sanitizer (ubuntu-24.04)

‘_return_value’ undeclared (first use in this function)

Check failure on line 916 in Modules/cmathmodule.c

View workflow job for this annotation

GitHub Actions / Ubuntu (free-threading) / build and test (ubuntu-24.04-arm)

‘_return_value’ undeclared (first use in this function)
if (value) {
PyObject_SetAttrString(exc, "value", value);
}
Py_DECREF(value);
PyErr_SetRaisedException(exc);
}
else if (errno == ERANGE) {
PyObject *exc = PyErr_GetRaisedException();
PyObject *value = PyComplex_FromCComplex(_return_value);

Check failure on line 925 in Modules/cmathmodule.c

View workflow job for this annotation

GitHub Actions / Windows (free-threading) / Build and test (arm64)

'_return_value': undeclared identifier [C:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check failure on line 925 in Modules/cmathmodule.c

View workflow job for this annotation

GitHub Actions / Windows (free-threading) / Build and test (arm64)

'function': cannot convert from 'int' to 'Py_complex' [C:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check warning on line 925 in Modules/cmathmodule.c

View workflow job for this annotation

GitHub Actions / Windows (free-threading) / Build and test (arm64)

'PyComplex_FromCComplex': different types for formal and actual parameter 1 [C:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check failure on line 925 in Modules/cmathmodule.c

View workflow job for this annotation

GitHub Actions / Windows / Build and test (x64)

'_return_value': undeclared identifier [D:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check failure on line 925 in Modules/cmathmodule.c

View workflow job for this annotation

GitHub Actions / Windows / Build and test (x64)

'function': cannot convert from 'int' to 'Py_complex' [D:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check warning on line 925 in Modules/cmathmodule.c

View workflow job for this annotation

GitHub Actions / Windows / Build and test (x64)

'PyComplex_FromCComplex': different types for formal and actual parameter 1 [D:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check failure on line 925 in Modules/cmathmodule.c

View workflow job for this annotation

GitHub Actions / Windows (free-threading) / Build and test (x64)

'_return_value': undeclared identifier [D:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check failure on line 925 in Modules/cmathmodule.c

View workflow job for this annotation

GitHub Actions / Windows (free-threading) / Build and test (x64)

'function': cannot convert from 'int' to 'Py_complex' [D:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check warning on line 925 in Modules/cmathmodule.c

View workflow job for this annotation

GitHub Actions / Windows (free-threading) / Build and test (x64)

'PyComplex_FromCComplex': different types for formal and actual parameter 1 [D:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check failure on line 925 in Modules/cmathmodule.c

View workflow job for this annotation

GitHub Actions / Windows / Build and test (arm64)

'_return_value': undeclared identifier [C:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check failure on line 925 in Modules/cmathmodule.c

View workflow job for this annotation

GitHub Actions / Windows / Build and test (arm64)

'function': cannot convert from 'int' to 'Py_complex' [C:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check warning on line 925 in Modules/cmathmodule.c

View workflow job for this annotation

GitHub Actions / Windows / Build and test (arm64)

'PyComplex_FromCComplex': different types for formal and actual parameter 1 [C:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]
if (value) {
PyObject_SetAttrString(exc, "value", value);
}
Py_DECREF(value);
PyErr_SetRaisedException(exc);
}
else /* Unexpected math error */
PyErr_SetFromErrno(PyExc_ValueError);
return NULL;
Expand Down Expand Up @@ -1338,3 +1364,33 @@
{
return PyModuleDef_Init(&cmathmodule);
}

static int
Py_complex_protected_return_converter(PyObject *obj, void *ptr)

Check warning on line 1369 in Modules/cmathmodule.c

View workflow job for this annotation

GitHub Actions / Cross build Linux

‘Py_complex_protected_return_converter’ defined but not used [-Wunused-function]

Check warning on line 1369 in Modules/cmathmodule.c

View workflow job for this annotation

GitHub Actions / Hypothesis tests on Ubuntu

‘Py_complex_protected_return_converter’ defined but not used [-Wunused-function]

Check warning on line 1369 in Modules/cmathmodule.c

View workflow job for this annotation

GitHub Actions / Ubuntu (free-threading) / build and test (ubuntu-24.04)

‘Py_complex_protected_return_converter’ defined but not used [-Wunused-function]

Check warning on line 1369 in Modules/cmathmodule.c

View workflow job for this annotation

GitHub Actions / Ubuntu / build and test (ubuntu-24.04)

‘Py_complex_protected_return_converter’ defined but not used [-Wunused-function]

Check warning on line 1369 in Modules/cmathmodule.c

View workflow job for this annotation

GitHub Actions / Ubuntu (bolt) / build and test (ubuntu-24.04)

‘Py_complex_protected_return_converter’ defined but not used [-Wunused-function]

Check warning on line 1369 in Modules/cmathmodule.c

View workflow job for this annotation

GitHub Actions / Ubuntu / build and test (ubuntu-24.04-arm)

‘Py_complex_protected_return_converter’ defined but not used [-Wunused-function]

Check warning on line 1369 in Modules/cmathmodule.c

View workflow job for this annotation

GitHub Actions / Address sanitizer (ubuntu-24.04)

‘Py_complex_protected_return_converter’ defined but not used [-Wunused-function]

Check warning on line 1369 in Modules/cmathmodule.c

View workflow job for this annotation

GitHub Actions / Ubuntu (free-threading) / build and test (ubuntu-24.04-arm)

‘Py_complex_protected_return_converter’ defined but not used [-Wunused-function]
{
Py_complex *p = (Py_complex *)ptr;
if (PyComplex_Check(obj)) {
*p = PyComplex_AsCComplex(obj);
return 1;
}
if (PyFloat_Check(obj)) {
p->real = PyFloat_AsDouble(obj);
p->imag = 0.0;
return 1;
}
if (PyLong_Check(obj)) {
p->real = PyLong_AsDouble(obj);
p->imag = 0.0;
return 1;
}
if (PyErr_Occurred()) {
PyObject *exc = PyErr_GetRaisedException();
PyObject *value = PyComplex_FromCComplex(*p);
if (value) {
PyObject_SetAttrString(exc, "value", value);
}
Py_DECREF(value);
PyErr_SetRaisedException(exc);
}
return 0;
}
57 changes: 22 additions & 35 deletions Modules/mathmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ module math
Double and triple length extended precision algorithms from:

Accurate Sum and Dot Product
by Takeshi Ogita, Siegfried M. Rump, and ShinIchi Oishi
by Takeshi Ogita, Siegfried M. Rump, and Shin'Ichi Oishi
https://doi.org/10.1137/030601818
https://www.tuhh.de/ti3/paper/rump/OgRuOi05.pdf

Expand Down Expand Up @@ -847,44 +847,31 @@ math_lcm_impl(PyObject *module, PyObject * const *args,
static int
is_error(double x, int raise_edom)
{
int result = 1; /* presumption of guilt */
assert(errno); /* non-zero errno is a precondition for calling */
if (errno == EDOM) {
if (Py_IS_NAN(x)) {
if (raise_edom) {
PyErr_SetString(PyExc_ValueError, "math domain error");
PyObject *exc = PyErr_GetRaisedException();
PyObject *value = PyFloat_FromDouble(x);
if (value) {
PyObject_SetAttrString(exc, "value", value);
}
Py_DECREF(value);
PyErr_SetRaisedException(exc);
}
return 1;
}

else if (errno == ERANGE) {
/* ANSI C generally requires libm functions to set ERANGE
* on overflow, but also generally *allows* them to set
* ERANGE on underflow too. There's no consistency about
* the latter across platforms.
* Alas, C99 never requires that errno be set.
* Here we suppress the underflow errors (libm functions
* should return a zero on underflow, and +- HUGE_VAL on
* overflow, so testing the result for zero suffices to
* distinguish the cases).
*
* On some platforms (Ubuntu/ia64) it seems that errno can be
* set to ERANGE for subnormal results that do *not* underflow
* to zero. So to be safe, we'll ignore ERANGE whenever the
* function result is less than 1.5 in absolute value.
*
* bpo-46018: Changed to 1.5 to ensure underflows in expm1()
* are correctly detected, since the function may underflow
* toward -1.0 rather than 0.0.
*/
if (fabs(x) < 1.5)
result = 0;
else
PyErr_SetString(PyExc_OverflowError,
"math range error");
if (Py_IS_INFINITY(x)) {
if (raise_edom) {
PyObject *exc = PyErr_GetRaisedException();
PyObject *value = PyFloat_FromDouble(x);
if (value) {
PyObject_SetAttrString(exc, "value", value);
}
Py_DECREF(value);
PyErr_SetRaisedException(exc);
}
return 1;
}
else
/* Unexpected math error */
PyErr_SetFromErrno(PyExc_ValueError);
return result;
return 0;
}

/*
Expand Down
Loading