From 53ddec42a3d193f58d13f281657fbe906185ee44 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 15 Nov 2023 05:02:51 +0100 Subject: [PATCH] gh-112019: Add Py_complex_abs() function Add functions operating on Py_complex numbers: * Py_complex_sum() * Py_complex_diff() * Py_complex_neg() * Py_complex_prod() * Py_complex_quot() * Py_complex_pow() * Py_complex_abs() Add basic tests on these functions in test_capi. --- Doc/c-api/complex.rst | 34 +++++++++++--- Doc/whatsnew/3.13.rst | 14 ++++++ Include/cpython/complexobject.h | 19 ++++++++ Include/internal/pycore_complexobject.h | 10 ---- ...-11-15-05-45-59.gh-issue-111481.BhE88s.rst | 11 +++++ Modules/_testcapi/complex.c | 47 +++++++++++++++++++ Modules/cmathmodule.c | 13 +++-- Objects/complexobject.c | 34 +++++++------- 8 files changed, 142 insertions(+), 40 deletions(-) create mode 100644 Misc/NEWS.d/next/C API/2023-11-15-05-45-59.gh-issue-111481.BhE88s.rst diff --git a/Doc/c-api/complex.rst b/Doc/c-api/complex.rst index e3fd001c599c80..c07ab0eefd5f8b 100644 --- a/Doc/c-api/complex.rst +++ b/Doc/c-api/complex.rst @@ -34,31 +34,31 @@ pointers. This is consistent throughout the API. } Py_complex; -.. c:function:: Py_complex _Py_c_sum(Py_complex left, Py_complex right) +.. c:function:: Py_complex Py_complex_sum(Py_complex left, Py_complex right) Return the sum of two complex numbers, using the C :c:type:`Py_complex` representation. -.. c:function:: Py_complex _Py_c_diff(Py_complex left, Py_complex right) +.. c:function:: Py_complex Py_complex_diff(Py_complex left, Py_complex right) Return the difference between two complex numbers, using the C :c:type:`Py_complex` representation. -.. c:function:: Py_complex _Py_c_neg(Py_complex num) +.. c:function:: Py_complex Py_complex_neg(Py_complex num) Return the negation of the complex number *num*, using the C :c:type:`Py_complex` representation. -.. c:function:: Py_complex _Py_c_prod(Py_complex left, Py_complex right) +.. c:function:: Py_complex Py_complex_prod(Py_complex left, Py_complex right) Return the product of two complex numbers, using the C :c:type:`Py_complex` representation. -.. c:function:: Py_complex _Py_c_quot(Py_complex dividend, Py_complex divisor) +.. c:function:: Py_complex Py_complex_quot(Py_complex dividend, Py_complex divisor) Return the quotient of two complex numbers, using the C :c:type:`Py_complex` representation. @@ -67,7 +67,7 @@ pointers. This is consistent throughout the API. :c:data:`errno` to :c:macro:`!EDOM`. -.. c:function:: Py_complex _Py_c_pow(Py_complex num, Py_complex exp) +.. c:function:: Py_complex Py_complex_pow(Py_complex num, Py_complex exp) Return the exponentiation of *num* by *exp*, using the C :c:type:`Py_complex` representation. @@ -76,6 +76,28 @@ pointers. This is consistent throughout the API. this method returns zero and sets :c:data:`errno` to :c:macro:`!EDOM`. +.. c:function:: double Py_complex_abs(Py_complex num) + + Return the absolute value (or modulus or magnitude) of *num*, using the C + :c:type:`Py_complex` representation. + + .. versionadded:: 3.5 + + +In Python 3.13, these functions have been made public. Previously, they were +known as these private functions: + +* ``Py_complex_sum()``: ``_Py_c_sum()`` +* ``Py_complex_diff()``: ``_Py_c_diff()`` +* ``Py_complex_neg()``: ``_Py_c_neg()`` +* ``Py_complex_prod()``: ``_Py_c_prod()`` +* ``Py_complex_quot()``: ``_Py_c_quot()`` +* ``Py_complex_pow()``: ``_Py_c_pow()`` +* ``Py_complex_abs()``: ``_Py_c_abs()`` + +Old names are kept for backward compatibility. + + Complex Numbers as Python Objects ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index 136fe901ce39fb..0fe77a63c2de4d 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -1181,6 +1181,20 @@ New Features :exc:`KeyError` if the key missing. (Contributed by Stefan Behnel and Victor Stinner in :gh:`111262`.) +* Add public functions operating on ``Py_complex`` numbers: + + * :c:func:`Py_complex_sum`: previously known as ``_Py_c_sum()`` + * :c:func:`Py_complex_diff`: previously known as ``_Py_c_diff()`` + * :c:func:`Py_complex_neg`: previously known as ``_Py_c_neg()`` + * :c:func:`Py_complex_prod`: previously known as ``_Py_c_prod()`` + * :c:func:`Py_complex_quot`: previously known as ``_Py_c_quot()`` + * :c:func:`Py_complex_pow`: previously known as ``_Py_c_pow()`` + * :c:func:`Py_complex_abs`: previously known as ``_Py_c_abs()`` + + The old names with the ``_Py_c`` prefix are kept for backward compatibility. + + (Contributed by Victor Stinner in :gh:`111481`.) + Porting to Python 3.13 ---------------------- diff --git a/Include/cpython/complexobject.h b/Include/cpython/complexobject.h index b524ec42c24371..533f2d0acfa4e9 100644 --- a/Include/cpython/complexobject.h +++ b/Include/cpython/complexobject.h @@ -7,6 +7,25 @@ typedef struct { double imag; } Py_complex; +// Operations on complex numbers. +PyAPI_FUNC(Py_complex) Py_complex_sum(Py_complex, Py_complex); +PyAPI_FUNC(Py_complex) Py_complex_diff(Py_complex, Py_complex); +PyAPI_FUNC(Py_complex) Py_complex_neg(Py_complex); +PyAPI_FUNC(Py_complex) Py_complex_prod(Py_complex, Py_complex); +PyAPI_FUNC(Py_complex) Py_complex_quot(Py_complex, Py_complex); +PyAPI_FUNC(Py_complex) Py_complex_pow(Py_complex, Py_complex); +PyAPI_FUNC(double) Py_complex_abs(Py_complex); + +// Keep old Python 3.12 names as aliases to new functions +#define _Py_c_sum Py_complex_sum +#define _Py_c_diff Py_complex_diff +#define _Py_c_neg Py_complex_neg +#define _Py_c_prod Py_complex_prod +#define _Py_c_quot Py_complex_quot +#define _Py_c_pow Py_complex_pow +#define _Py_c_abs Py_complex_abs + + /* Complex object interface */ /* diff --git a/Include/internal/pycore_complexobject.h b/Include/internal/pycore_complexobject.h index a6fee9d23f3a9f..54713536eedc46 100644 --- a/Include/internal/pycore_complexobject.h +++ b/Include/internal/pycore_complexobject.h @@ -10,16 +10,6 @@ extern "C" { #include "pycore_unicodeobject.h" // _PyUnicodeWriter -// Operations on complex numbers. -// Export functions for 'cmath' shared extension. -PyAPI_FUNC(Py_complex) _Py_c_sum(Py_complex, Py_complex); -PyAPI_FUNC(Py_complex) _Py_c_diff(Py_complex, Py_complex); -PyAPI_FUNC(Py_complex) _Py_c_neg(Py_complex); -PyAPI_FUNC(Py_complex) _Py_c_prod(Py_complex, Py_complex); -PyAPI_FUNC(Py_complex) _Py_c_quot(Py_complex, Py_complex); -PyAPI_FUNC(Py_complex) _Py_c_pow(Py_complex, Py_complex); -PyAPI_FUNC(double) _Py_c_abs(Py_complex); - /* Format the object based on the format_spec, as defined in PEP 3101 (Advanced String Formatting). */ extern int _PyComplex_FormatAdvancedWriter( diff --git a/Misc/NEWS.d/next/C API/2023-11-15-05-45-59.gh-issue-111481.BhE88s.rst b/Misc/NEWS.d/next/C API/2023-11-15-05-45-59.gh-issue-111481.BhE88s.rst new file mode 100644 index 00000000000000..eb5bd79ba46543 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2023-11-15-05-45-59.gh-issue-111481.BhE88s.rst @@ -0,0 +1,11 @@ +Add public functions operating on ``Py_complex`` numbers: + +* :c:func:`Py_complex_sum`: previously known as ``_Py_c_sum()`` +* :c:func:`Py_complex_diff`: previously known as ``_Py_c_diff()`` +* :c:func:`Py_complex_neg`: previously known as ``_Py_c_neg()`` +* :c:func:`Py_complex_prod`: previously known as ``_Py_c_prod()`` +* :c:func:`Py_complex_quot`: previously known as ``_Py_c_quot()`` +* :c:func:`Py_complex_pow`: previously known as ``_Py_c_pow()`` +* :c:func:`Py_complex_abs`: previously known as ``_Py_c_abs()`` + +The old names with the ``_Py_c`` prefix are kept for backward compatibility. diff --git a/Modules/_testcapi/complex.c b/Modules/_testcapi/complex.c index 400f4054c613ee..8bdfe521dfee0d 100644 --- a/Modules/_testcapi/complex.c +++ b/Modules/_testcapi/complex.c @@ -86,6 +86,52 @@ complex_asccomplex(PyObject *Py_UNUSED(module), PyObject *obj) } +static PyObject * +test_py_complex(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) +{ + // Basic tests on Py_complex functions: + // + // - Py_complex_sum() + // - Py_complex_diff() + // - Py_complex_neg() + // - Py_complex_prod() + // - Py_complex_quot() + // - Py_complex_pow() + // - Py_complex_abs() + + Py_complex a = {1.0, 5.0}; + Py_complex b = {1.5, 0.5}; + Py_complex x = Py_complex_sum(a, b); + assert(x.real == 2.5 && x.imag == 5.5); + + x = Py_complex_diff(a, b); + assert(x.real == -0.5 && x.imag == 4.5); + + x = Py_complex_neg(a); + assert(x.real == -1.0 && x.imag == -5.0); + + a = (Py_complex){1.0, -2.0}; + b = (Py_complex){3.0, 4.0}; + x = Py_complex_prod(a, b); + assert(x.real == 11.0 && x.imag == -2.0); + + a = (Py_complex){6.9, -3.2}; + x = Py_complex_quot(a, a); + assert(x.real == 1.0 && x.imag == 0.0); + + a = (Py_complex){1.3, 2.7}; + Py_complex zero = {0.0, 0.0}; + x = Py_complex_pow(a, zero); + assert(x.real == 1.0 && x.imag == 0.0); + + x = (Py_complex){3.0, 4.0}; + double mod = Py_complex_abs(x); + assert(mod == hypot(3.0, 4.0)); + + Py_RETURN_NONE; +} + + static PyMethodDef test_methods[] = { {"complex_check", complex_check, METH_O}, {"complex_checkexact", complex_checkexact, METH_O}, @@ -94,6 +140,7 @@ static PyMethodDef test_methods[] = { {"complex_realasdouble", complex_realasdouble, METH_O}, {"complex_imagasdouble", complex_imagasdouble, METH_O}, {"complex_asccomplex", complex_asccomplex, METH_O}, + {"test_py_complex", test_py_complex, METH_NOARGS}, {NULL}, }; diff --git a/Modules/cmathmodule.c b/Modules/cmathmodule.c index 57bc55632be485..3971ea00bd8754 100644 --- a/Modules/cmathmodule.c +++ b/Modules/cmathmodule.c @@ -7,7 +7,6 @@ #endif #include "Python.h" -#include "pycore_complexobject.h" // _Py_c_neg() #include "pycore_pymath.h" // _PY_SHORT_FLOAT_REPR /* we need DBL_MAX, DBL_MIN, DBL_EPSILON, DBL_MANT_DIG and FLT_RADIX from float.h. We assume that FLT_RADIX is either 2 or 16. */ @@ -374,7 +373,7 @@ cmath_atanh_impl(PyObject *module, Py_complex z) /* Reduce to case where z.real >= 0., using atanh(z) = -atanh(-z). */ if (z.real < 0.) { - return _Py_c_neg(cmath_atanh_impl(module, _Py_c_neg(z))); + return Py_complex_neg(cmath_atanh_impl(module, Py_complex_neg(z))); } ay = fabs(z.imag); @@ -927,7 +926,7 @@ cmath_log_impl(PyObject *module, Py_complex x, PyObject *y_obj) return NULL; } y = c_log(y); - x = _Py_c_quot(x, y); + x = Py_complex_quot(x, y); } if (errno != 0) return math_error(); @@ -992,7 +991,7 @@ cmath_polar_impl(PyObject *module, Py_complex z) errno = 0; phi = c_atan2(z); /* should not cause any exception */ - r = _Py_c_abs(z); /* sets errno to ERANGE on overflow */ + r = Py_complex_abs(z); /* sets errno to ERANGE on overflow */ if (errno != 0) return math_error(); else @@ -1176,10 +1175,10 @@ cmath_isclose_impl(PyObject *module, Py_complex a, Py_complex b, this is essentially the "weak" test from the Boost library */ - diff = _Py_c_abs(_Py_c_diff(a, b)); + diff = Py_complex_abs(Py_complex_diff(a, b)); - return (((diff <= rel_tol * _Py_c_abs(b)) || - (diff <= rel_tol * _Py_c_abs(a))) || + return (((diff <= rel_tol * Py_complex_abs(b)) || + (diff <= rel_tol * Py_complex_abs(a))) || (diff <= abs_tol)); } diff --git a/Objects/complexobject.c b/Objects/complexobject.c index 0e96f54584677c..fb2bb82e10751d 100644 --- a/Objects/complexobject.c +++ b/Objects/complexobject.c @@ -26,7 +26,7 @@ class complex "PyComplexObject *" "&PyComplex_Type" static Py_complex c_1 = {1., 0.}; Py_complex -_Py_c_sum(Py_complex a, Py_complex b) +Py_complex_sum(Py_complex a, Py_complex b) { Py_complex r; r.real = a.real + b.real; @@ -35,7 +35,7 @@ _Py_c_sum(Py_complex a, Py_complex b) } Py_complex -_Py_c_diff(Py_complex a, Py_complex b) +Py_complex_diff(Py_complex a, Py_complex b) { Py_complex r; r.real = a.real - b.real; @@ -44,7 +44,7 @@ _Py_c_diff(Py_complex a, Py_complex b) } Py_complex -_Py_c_neg(Py_complex a) +Py_complex_neg(Py_complex a) { Py_complex r; r.real = -a.real; @@ -53,7 +53,7 @@ _Py_c_neg(Py_complex a) } Py_complex -_Py_c_prod(Py_complex a, Py_complex b) +Py_complex_prod(Py_complex a, Py_complex b) { Py_complex r; r.real = a.real*b.real - a.imag*b.imag; @@ -66,7 +66,7 @@ _Py_c_prod(Py_complex a, Py_complex b) #pragma optimize("", off) #endif Py_complex -_Py_c_quot(Py_complex a, Py_complex b) +Py_complex_quot(Py_complex a, Py_complex b) { /****************************************************************** This was the original algorithm. It's grossly prone to spurious @@ -127,7 +127,7 @@ _Py_c_quot(Py_complex a, Py_complex b) #endif Py_complex -_Py_c_pow(Py_complex a, Py_complex b) +Py_complex_pow(Py_complex a, Py_complex b) { Py_complex r; double vabs,len,at,phase; @@ -139,7 +139,7 @@ _Py_c_pow(Py_complex a, Py_complex b) if (b.imag != 0. || b.real < 0.) errno = EDOM; r.real = 0.; - r.imag = 0.; + r.imag = -1.; } else { vabs = hypot(a.real,a.imag); @@ -165,9 +165,9 @@ c_powu(Py_complex x, long n) p = x; while (mask > 0 && n >= mask) { if (n & mask) - r = _Py_c_prod(r,p); + r = Py_complex_prod(r,p); mask <<= 1; - p = _Py_c_prod(p,p); + p = Py_complex_prod(p,p); } return r; } @@ -178,12 +178,12 @@ c_powi(Py_complex x, long n) if (n > 0) return c_powu(x,n); else - return _Py_c_quot(c_1, c_powu(x,-n)); + return Py_complex_quot(c_1, c_powu(x,-n)); } double -_Py_c_abs(Py_complex z) +Py_complex_abs(Py_complex z) { /* sets errno = ERANGE on overflow; otherwise errno = 0 */ double result; @@ -462,7 +462,7 @@ complex_add(PyObject *v, PyObject *w) Py_complex a, b; TO_COMPLEX(v, a); TO_COMPLEX(w, b); - result = _Py_c_sum(a, b); + result = Py_complex_sum(a, b); return PyComplex_FromCComplex(result); } @@ -473,7 +473,7 @@ complex_sub(PyObject *v, PyObject *w) Py_complex a, b; TO_COMPLEX(v, a); TO_COMPLEX(w, b); - result = _Py_c_diff(a, b); + result = Py_complex_diff(a, b); return PyComplex_FromCComplex(result); } @@ -484,7 +484,7 @@ complex_mul(PyObject *v, PyObject *w) Py_complex a, b; TO_COMPLEX(v, a); TO_COMPLEX(w, b); - result = _Py_c_prod(a, b); + result = Py_complex_prod(a, b); return PyComplex_FromCComplex(result); } @@ -496,7 +496,7 @@ complex_div(PyObject *v, PyObject *w) TO_COMPLEX(v, a); TO_COMPLEX(w, b); errno = 0; - quot = _Py_c_quot(a, b); + quot = Py_complex_quot(a, b); if (errno == EDOM) { PyErr_SetString(PyExc_ZeroDivisionError, "complex division by zero"); return NULL; @@ -523,7 +523,7 @@ complex_pow(PyObject *v, PyObject *w, PyObject *z) p = c_powi(a, (long)b.real); } else { - p = _Py_c_pow(a, b); + p = Py_complex_pow(a, b); } _Py_ADJUST_ERANGE2(p.real, p.imag); @@ -564,7 +564,7 @@ complex_abs(PyComplexObject *v) { double result; - result = _Py_c_abs(v->cval); + result = Py_complex_abs(v->cval); if (errno == ERANGE) { PyErr_SetString(PyExc_OverflowError, 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