gh-69639: Add mixed-mode rules for complex arithmetic (C-like) (GH-124829)

"Generally, mixed-mode arithmetic combining real and complex variables should
be performed directly, not by first coercing the real to complex, lest the sign
of zero be rendered uninformative; the same goes for combinations of pure
imaginary quantities with complex variables." (c) Kahan, W: Branch cuts for
complex elementary functions.

This patch implements mixed-mode arithmetic rules, combining real and
complex variables as specified by C standards since C99 (in particular,
there is no special version for the true division with real lhs
operand).  Most C compilers implementing C99+ Annex G have only these
special rules (without support for imaginary type, which is going to be
deprecated in C2y).
This commit is contained in:
Sergey B Kirpichev 2024-11-26 18:57:39 +03:00 committed by GitHub
parent dcf629213b
commit 987311d42e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 449 additions and 98 deletions

View file

@ -7,6 +7,7 @@ from test.test_capi.test_getargs import (BadComplex, BadComplex2, Complex,
FloatSubclass, Float, BadFloat,
BadFloat2, ComplexSubclass)
from test.support import import_helper
from test.support.testcase import ComplexesAreIdenticalMixin
_testcapi = import_helper.import_module('_testcapi')
@ -23,7 +24,7 @@ class BadComplex3:
raise RuntimeError
class CAPIComplexTest(unittest.TestCase):
class CAPIComplexTest(ComplexesAreIdenticalMixin, unittest.TestCase):
def test_check(self):
# Test PyComplex_Check()
check = _testlimitedcapi.complex_check
@ -171,12 +172,33 @@ class CAPIComplexTest(unittest.TestCase):
self.assertEqual(_py_c_sum(1, 1j), (1+1j, 0))
def test_py_cr_sum(self):
# Test _Py_cr_sum()
_py_cr_sum = _testcapi._py_cr_sum
self.assertComplexesAreIdentical(_py_cr_sum(-0j, -0.0)[0],
complex(-0.0, -0.0))
def test_py_c_diff(self):
# Test _Py_c_diff()
_py_c_diff = _testcapi._py_c_diff
self.assertEqual(_py_c_diff(1, 1j), (1-1j, 0))
def test_py_cr_diff(self):
# Test _Py_cr_diff()
_py_cr_diff = _testcapi._py_cr_diff
self.assertComplexesAreIdentical(_py_cr_diff(-0j, 0.0)[0],
complex(-0.0, -0.0))
def test_py_rc_diff(self):
# Test _Py_rc_diff()
_py_rc_diff = _testcapi._py_rc_diff
self.assertComplexesAreIdentical(_py_rc_diff(-0.0, 0j)[0],
complex(-0.0, -0.0))
def test_py_c_neg(self):
# Test _Py_c_neg()
_py_c_neg = _testcapi._py_c_neg
@ -189,6 +211,13 @@ class CAPIComplexTest(unittest.TestCase):
self.assertEqual(_py_c_prod(2, 1j), (2j, 0))
def test_py_cr_prod(self):
# Test _Py_cr_prod()
_py_cr_prod = _testcapi._py_cr_prod
self.assertComplexesAreIdentical(_py_cr_prod(complex('inf+1j'), INF)[0],
complex('inf+infj'))
def test_py_c_quot(self):
# Test _Py_c_quot()
_py_c_quot = _testcapi._py_c_quot
@ -211,6 +240,20 @@ class CAPIComplexTest(unittest.TestCase):
self.assertEqual(_py_c_quot(1, 0j)[1], errno.EDOM)
def test_py_cr_quot(self):
# Test _Py_cr_quot()
_py_cr_quot = _testcapi._py_cr_quot
self.assertComplexesAreIdentical(_py_cr_quot(complex('inf+1j'), 2**1000)[0],
INF + 2**-1000*1j)
def test_py_rc_quot(self):
# Test _Py_rc_quot()
_py_rc_quot = _testcapi._py_rc_quot
self.assertComplexesAreIdentical(_py_rc_quot(1.0, complex('nan-infj'))[0],
0j)
def test_py_c_pow(self):
# Test _Py_c_pow()
_py_c_pow = _testcapi._py_c_pow