Skip to content

Commit

Permalink
pythongh-112019: Add Py_complex_abs() function
Browse files Browse the repository at this point in the history
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()
  • Loading branch information
vstinner committed Nov 15, 2023
1 parent 55f3cce commit 78842d7
Show file tree
Hide file tree
Showing 8 changed files with 142 additions and 40 deletions.
34 changes: 28 additions & 6 deletions Doc/c-api/complex.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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.
Expand All @@ -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
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
14 changes: 14 additions & 0 deletions Doc/whatsnew/3.13.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
----------------------
Expand Down
19 changes: 19 additions & 0 deletions Include/cpython/complexobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */

/*
Expand Down
10 changes: 0 additions & 10 deletions Include/internal/pycore_complexobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
47 changes: 47 additions & 0 deletions Modules/_testcapi/complex.c
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand All @@ -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},
};

Expand Down
13 changes: 6 additions & 7 deletions Modules/cmathmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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));
}

Expand Down

0 comments on commit 78842d7

Please sign in to comment.