Skip to content

Commit c4e75d1

Browse files
committed
pythongh-109218: Imaginary type and IEC 60559-compatible complex arithmetic
"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. That's why C standards since C99 introduce imaginary types. This patch proposes similar extension to the Python language: * Added a new subtype (imaginary) of the complex type. New type has few overloaded methods (conjugate() and __getnewargs__()). * Complex and imaginary types implement IEC 60559-compatible complex arithmetic (as specified by C11 Annex G). * Imaginary literals now produce instances of imaginary type. * cmath.infj/nanj were changed to be of imaginary type. * Modules ast, code, copy, marshal got support for imaginary type. * Few tests adapted to use complex, instead of imaginary literals - Lib/test/test_fractions.py - Lib/test/test_socket.py - Lib/test/test_str.py Lets consider some (actually interrelated) problems, shown for unpatched code, which could be solved on this way. 1) First, new code allows to use complex arithmetic for implementation of mathematical functions without special "corner cases". Take the inverse hyperbolic sine as an example: >>> z = complex(-0.0, 2) >>> cmath.asinh(z) (-1.3169578969248166+1.5707963267948966j) >>> # naive textbook formula doesn't work: >>> cmath.log(z + cmath.sqrt(1 + z*z)) (1.3169578969248166+1.5707963267948966j) >>> # "fixed" version does: >>> cmath.log(z + cmath.sqrt(complex(1 + (z*z).real, (z*z).imag))) (-1.3169578969248164+1.5707963267948966j) 2) Previously, we have only unsigned imaginary literals with the following semantics: ±a±bj = complex(±float(a), 0.0) ± complex(0.0, float(b)) While this behaviour was well documented, most users would expect instead here: ±a±bj = complex(±float(a), ±float(b)) i.e. that it follows to the rectangular notation for complex numbers. Things are worse, because the CPython docs sometimes asserts that the rectangular form is used and that some simple invariants holds. For example, sphinx docs for the complex class says: "complex(real=0, imag=0) ... Return a complex number with the value real + imag*1j ...". But: >>> complex(0.0, cmath.inf) infj >>> 0.0 + cmath.inf*1j (nan+infj) 3) The ``eval(repr(x)) == x`` invariant was broken for the complex type. Below are simple examples with signed zero: >>> complex(-0.0, 1.0) # also note funny signed integer zero below (-0+1j) >>> -0+1j 1j >> -(0.0-1j) # "correct" input for above with Python numeric literals (-0+1j) >>> -(0-1j) # also "correct"; integer 0 matters! (-0+1j) >>> complex(1.0, -0.0) (1-0j) >>> 1-0j (1+0j) >>> -(-1 + 0j) (1-0j) Similar is true for complex numbers with other special components: >>> complex(0.0, -cmath.inf) -infj >>> -cmath.infj (-0-infj)
1 parent 0e3c8cd commit c4e75d1

21 files changed

+747
-92
lines changed

Include/complexobject.h

+6
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,21 @@ extern "C" {
99
/* Complex object interface */
1010

1111
PyAPI_DATA(PyTypeObject) PyComplex_Type;
12+
PyAPI_DATA(PyTypeObject) PyImaginary_Type;
1213

1314
#define PyComplex_Check(op) PyObject_TypeCheck((op), &PyComplex_Type)
1415
#define PyComplex_CheckExact(op) Py_IS_TYPE((op), &PyComplex_Type)
1516

17+
#define PyImaginary_Check(op) PyObject_TypeCheck((op), &PyImaginary_Type)
18+
#define PyImaginary_CheckExact(op) Py_IS_TYPE((op), &PyImaginary_Type)
19+
1620
PyAPI_FUNC(PyObject *) PyComplex_FromDoubles(double real, double imag);
1721

1822
PyAPI_FUNC(double) PyComplex_RealAsDouble(PyObject *op);
1923
PyAPI_FUNC(double) PyComplex_ImagAsDouble(PyObject *op);
2024

25+
PyAPI_FUNC(PyObject *) PyImaginary_FromDouble(double imag);
26+
2127
#ifndef Py_LIMITED_API
2228
# define Py_CPYTHON_COMPLEXOBJECT_H
2329
# include "cpython/complexobject.h"

Include/internal/pycore_floatobject.h

+2
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ extern PyObject* _Py_string_to_number_with_underscores(
5555

5656
extern double _Py_parse_inf_or_nan(const char *p, char **endptr);
5757

58+
extern int convert_to_double(PyObject **v, double *dbl);
59+
5860

5961
#ifdef __cplusplus
6062
}

Include/marshal.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ PyAPI_FUNC(PyObject *) PyMarshal_ReadObjectFromString(const char *,
1313
Py_ssize_t);
1414
PyAPI_FUNC(PyObject *) PyMarshal_WriteObjectToString(PyObject *, int);
1515

16-
#define Py_MARSHAL_VERSION 4
16+
#define Py_MARSHAL_VERSION 5
1717

1818
PyAPI_FUNC(long) PyMarshal_ReadLongFromFile(FILE *);
1919
PyAPI_FUNC(int) PyMarshal_ReadShortFromFile(FILE *);

Lib/ast.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ def _raise_malformed_node(node):
7474
msg += f' on line {lno}'
7575
raise ValueError(msg + f': {node!r}')
7676
def _convert_num(node):
77-
if not isinstance(node, Constant) or type(node.value) not in (int, float, complex):
77+
if not isinstance(node, Constant) or type(node.value) not in (int, float, imaginary, complex):
7878
_raise_malformed_node(node)
7979
return node.value
8080
def _convert_signed_num(node):

Lib/copy.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ def copy(x):
101101

102102
def _copy_immutable(x):
103103
return x
104-
for t in (types.NoneType, int, float, bool, complex, str, tuple,
104+
for t in (types.NoneType, int, float, bool, complex, imaginary, str, tuple,
105105
bytes, frozenset, type, range, slice, property,
106106
types.BuiltinFunctionType, types.EllipsisType,
107107
types.NotImplementedType, types.FunctionType, types.CodeType,
@@ -178,6 +178,7 @@ def _deepcopy_atomic(x, memo):
178178
d[float] = _deepcopy_atomic
179179
d[bool] = _deepcopy_atomic
180180
d[complex] = _deepcopy_atomic
181+
d[imaginary] = _deepcopy_atomic
181182
d[bytes] = _deepcopy_atomic
182183
d[str] = _deepcopy_atomic
183184
d[types.CodeType] = _deepcopy_atomic

Lib/test/test_complex.py

+133-25
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,23 @@
2121
(1, 0+0j),
2222
)
2323

24+
class float2:
25+
def __init__(self, value):
26+
self.value = value
27+
def __float__(self):
28+
return self.value
29+
30+
class MyIndex:
31+
def __init__(self, value):
32+
self.value = value
33+
def __index__(self):
34+
return self.value
35+
36+
class MyInt:
37+
def __int__(self):
38+
return 42
39+
40+
2441
class ComplexTest(unittest.TestCase):
2542

2643
def assertAlmostEqual(self, a, b):
@@ -73,6 +90,10 @@ def assertFloatsAreIdentical(self, x, y):
7390
msg += ': zeros have different signs'
7491
self.fail(msg.format(x, y))
7592

93+
def assertComplexesAreIdentical(self, x, y):
94+
self.assertFloatsAreIdentical(x.real, y.real)
95+
self.assertFloatsAreIdentical(x.imag, y.imag)
96+
7697
def assertClose(self, x, y, eps=1e-9):
7798
"""Return true iff complexes x and y "are close"."""
7899
self.assertCloseAbs(x.real, y.real, eps)
@@ -118,6 +139,26 @@ def test_truediv(self):
118139
self.assertTrue(isnan(z.real))
119140
self.assertTrue(isnan(z.imag))
120141

142+
self.assertEqual((1+1j)/float(2), 0.5+0.5j)
143+
self.assertRaises(TypeError, operator.truediv, None, 1+1j)
144+
self.assertRaises(TypeError, operator.truediv, 1+1j, None)
145+
146+
self.assertEqual(1j/imaginary(2), 0.5)
147+
self.assertEqual((1+1j)/imaginary(2), 0.5-0.5j)
148+
self.assertEqual(1j/complex(1, 1), 0.5+0.5j)
149+
self.assertEqual(1j/float(2), 0.5j)
150+
self.assertEqual(float(1)/(1+2j), 0.2-0.4j)
151+
self.assertEqual(float(1)/(-1+2j), -0.2-0.4j)
152+
self.assertEqual(float(1)/(1-2j), 0.2+0.4j)
153+
154+
z = float(1)/(NAN+2j)
155+
self.assertTrue(isnan(z.real))
156+
self.assertTrue(isnan(z.imag))
157+
158+
self.assertRaises(ZeroDivisionError, operator.truediv, 1j, 0j)
159+
self.assertRaises(ZeroDivisionError, operator.truediv, 1j, complex(0, 0))
160+
self.assertRaises(ZeroDivisionError, operator.truediv, 1j, 0.0)
161+
121162
def test_truediv_zero_division(self):
122163
for a, b in ZERO_DIVISION:
123164
with self.assertRaises(ZeroDivisionError):
@@ -188,24 +229,83 @@ def check(n, deltas, is_equal, imag = 0.0):
188229
def test_add(self):
189230
self.assertEqual(1j + int(+1), complex(+1, 1))
190231
self.assertEqual(1j + int(-1), complex(-1, 1))
232+
self.assertEqual(1j + imaginary(1), imaginary(2))
191233
self.assertRaises(OverflowError, operator.add, 1j, 10**1000)
192234
self.assertRaises(TypeError, operator.add, 1j, None)
235+
self.assertRaises(TypeError, operator.add, 1+1j, None)
193236
self.assertRaises(TypeError, operator.add, None, 1j)
194237

238+
self.assertComplexesAreIdentical(float(0.0) + 0j, complex(0, 0))
239+
self.assertComplexesAreIdentical(0j + float(0.0), complex(0, 0))
240+
self.assertComplexesAreIdentical(float(-0.0) + 0j, complex(-0.0, 0))
241+
self.assertComplexesAreIdentical(0j + float(-0.0), complex(-0.0, 0))
242+
self.assertComplexesAreIdentical((-0.0+0j) + float(0.0), complex(0, 0))
243+
self.assertComplexesAreIdentical(float(0.0) + (-0.0+0j), complex(0, 0))
244+
self.assertComplexesAreIdentical((1+0j) + complex(1-0j), complex(2, 0))
245+
self.assertComplexesAreIdentical(0j + complex(-0.0-0j), complex(-0.0, 0))
246+
self.assertComplexesAreIdentical(0j + complex(-0j), complex(0, 0))
247+
self.assertComplexesAreIdentical((1+0j) + complex(-0.0-0j), complex(1, 0))
248+
self.assertComplexesAreIdentical(complex(-0.0+0j) + (-0j), complex(-0.0, 0))
249+
self.assertComplexesAreIdentical((1+0j) + float(1.0), complex(2, 0))
250+
self.assertComplexesAreIdentical(float(1.0) + (1+0j), complex(2, 0))
251+
self.assertComplexesAreIdentical((1-0j) + float(1.0), complex(2, -0.0))
252+
self.assertComplexesAreIdentical(float(1.0) + (1-0j), complex(2, -0.0))
253+
254+
195255
def test_sub(self):
196256
self.assertEqual(1j - int(+1), complex(-1, 1))
197257
self.assertEqual(1j - int(-1), complex(1, 1))
258+
self.assertEqual(1j - imaginary(2), imaginary(-1))
198259
self.assertRaises(OverflowError, operator.sub, 1j, 10**1000)
199260
self.assertRaises(TypeError, operator.sub, 1j, None)
261+
self.assertRaises(TypeError, operator.sub, 1+1j, None)
262+
self.assertRaises(TypeError, operator.sub, None, 1+1j)
200263
self.assertRaises(TypeError, operator.sub, None, 1j)
264+
self.assertRaises(TypeError, operator.sub, 1j, None)
265+
266+
self.assertComplexesAreIdentical(float(0.0) - 0j, complex(0, -0.0))
267+
self.assertComplexesAreIdentical(0j - float(0.0), complex(-0.0, 0))
268+
self.assertComplexesAreIdentical(float(-0.0) - 0j, complex(-0.0, -0.0))
269+
self.assertComplexesAreIdentical(0j - float(-0.0), complex(0, 0))
270+
self.assertComplexesAreIdentical((-0.0+0j) - float(0.0), complex(-0.0, 0))
271+
self.assertComplexesAreIdentical(float(0.0) - (-0.0+0j), complex(0, -0.0))
272+
self.assertComplexesAreIdentical((1+0j) - complex(1-0j), complex(0, 0))
273+
self.assertComplexesAreIdentical(0j - complex(-0.0-0j), complex(0, 0))
274+
self.assertComplexesAreIdentical(0j - complex(-0j), complex(-0.0, 0))
275+
self.assertComplexesAreIdentical((1+0j) - complex(-0.0-0j), complex(1, 0))
276+
self.assertComplexesAreIdentical(complex(-0.0+0j) - (-0j), complex(-0.0, 0))
277+
self.assertComplexesAreIdentical((1+0j) - float(1.0), complex(0, 0))
278+
self.assertComplexesAreIdentical(float(1.0) - (1+0j), complex(0, -0.0))
279+
self.assertComplexesAreIdentical((1-0j) - float(1.0), complex(0, -0.0))
280+
self.assertComplexesAreIdentical(float(1.0) - (1-0j), complex(0, 0))
201281

202282
def test_mul(self):
203283
self.assertEqual(1j * int(20), complex(0, 20))
204284
self.assertEqual(1j * int(-1), complex(0, -1))
285+
self.assertEqual(2j * imaginary(3), -6.0)
205286
self.assertRaises(OverflowError, operator.mul, 1j, 10**1000)
206287
self.assertRaises(TypeError, operator.mul, 1j, None)
288+
self.assertRaises(TypeError, operator.mul, 1+1j, None)
207289
self.assertRaises(TypeError, operator.mul, None, 1j)
208290

291+
self.assertComplexesAreIdentical(float(0.0) * 0j, complex(0, 0))
292+
self.assertComplexesAreIdentical(0j * float(0.0), complex(0.0, 0))
293+
self.assertComplexesAreIdentical(float(-0.0) * 0j, complex(0.0, -0.0))
294+
self.assertComplexesAreIdentical(0j * float(-0.0), complex(0, -0.0))
295+
self.assertComplexesAreIdentical((-0.0+0j) * float(0.0), complex(-0.0, 0))
296+
self.assertComplexesAreIdentical(float(0.0) * (-0.0+0j), complex(-0.0, 0))
297+
self.assertComplexesAreIdentical((-0.0+0j) * float(-0.0), complex(0, -0.0))
298+
self.assertComplexesAreIdentical(float(-0.0) * (-0.0+0j), complex(0, -0.0))
299+
self.assertComplexesAreIdentical((-0.0-0j) * float(-0.0), complex(0, 0))
300+
self.assertComplexesAreIdentical(float(-0.0) * (-0.0-0j), complex(0, 0))
301+
self.assertComplexesAreIdentical((1+0j) * complex(1-0j), complex(1, 0))
302+
self.assertComplexesAreIdentical(0j * complex(-0.0-0j), complex(0.0, -0.0))
303+
self.assertComplexesAreIdentical(0j * complex(-0j), complex(0, 0))
304+
self.assertComplexesAreIdentical((1+0j) * complex(-0.0-0j), complex(0, -0.0))
305+
self.assertComplexesAreIdentical((-0.0+0j) * complex(-0j), complex(0, 0))
306+
self.assertComplexesAreIdentical((1+0j) * float(1.0), complex(1, 0))
307+
self.assertComplexesAreIdentical(float(1.0) * (1+0j), complex(1, 0))
308+
209309
def test_mod(self):
210310
# % is no longer supported on complex numbers
211311
with self.assertRaises(TypeError):
@@ -338,6 +438,7 @@ def test_boolcontext(self):
338438

339439
def test_conjugate(self):
340440
self.assertClose(complex(5.3, 9.8).conjugate(), 5.3-9.8j)
441+
self.assertEqual(1j.conjugate(), -1j)
341442

342443
def test_constructor(self):
343444
class NS:
@@ -481,31 +582,15 @@ def __complex__(self):
481582

482583
self.assertRaises(EvilExc, complex, evilcomplex())
483584

484-
class float2:
485-
def __init__(self, value):
486-
self.value = value
487-
def __float__(self):
488-
return self.value
489-
490585
self.assertAlmostEqual(complex(float2(42.)), 42)
491586
self.assertAlmostEqual(complex(real=float2(17.), imag=float2(23.)), 17+23j)
492587
self.assertRaises(TypeError, complex, float2(None))
493588

494-
class MyIndex:
495-
def __init__(self, value):
496-
self.value = value
497-
def __index__(self):
498-
return self.value
499-
500589
self.assertAlmostEqual(complex(MyIndex(42)), 42.0+0.0j)
501590
self.assertAlmostEqual(complex(123, MyIndex(42)), 123.0+42.0j)
502591
self.assertRaises(OverflowError, complex, MyIndex(2**2000))
503592
self.assertRaises(OverflowError, complex, 123, MyIndex(2**2000))
504593

505-
class MyInt:
506-
def __int__(self):
507-
return 42
508-
509594
self.assertRaises(TypeError, complex, MyInt())
510595
self.assertRaises(TypeError, complex, 123, MyInt())
511596

@@ -532,6 +617,23 @@ def __complex__(self):
532617
self.assertEqual(complex(complex1(1j)), 2j)
533618
self.assertRaises(TypeError, complex, complex2(1j))
534619

620+
def test_imaginary_constructor(self):
621+
self.assertEqual(imaginary(), 0j)
622+
self.assertEqual(imaginary(-2), -2j)
623+
self.assertEqual(imaginary(1.25), 1.25j)
624+
625+
self.assertEqual(imaginary(float2(42.)), 42j)
626+
self.assertRaises(TypeError, imaginary, float2(None))
627+
628+
self.assertEqual(imaginary(MyIndex(42)), 42j)
629+
self.assertRaises(OverflowError, imaginary, MyIndex(2**2000))
630+
631+
self.assertRaises(TypeError, imaginary, MyInt())
632+
self.assertRaises(TypeError, imaginary, 123, MyInt())
633+
634+
self.assertRaises(TypeError, imaginary, complex())
635+
self.assertRaises(TypeError, imaginary, object())
636+
535637
def test___complex__(self):
536638
z = 3 + 4j
537639
self.assertEqual(z.__complex__(), z)
@@ -647,19 +749,29 @@ def test(v, expected, test_fn=self.assertEqual):
647749
def test_pos(self):
648750
class ComplexSubclass(complex):
649751
pass
752+
class ImaginarySubclass(imaginary):
753+
pass
650754

651755
self.assertEqual(+(1+6j), 1+6j)
652756
self.assertEqual(+ComplexSubclass(1, 6), 1+6j)
653757
self.assertIs(type(+ComplexSubclass(1, 6)), complex)
654758

759+
self.assertEqual(+1j, 1j)
760+
self.assertEqual(+ImaginarySubclass(1), 1j)
761+
self.assertIs(type(+ImaginarySubclass(1)), imaginary)
762+
655763
def test_neg(self):
656764
self.assertEqual(-(1+6j), -1-6j)
765+
self.assertComplexesAreIdentical(-0j, complex(0, -0.0))
766+
self.assertComplexesAreIdentical(-complex(-0.0+0j), complex(0, -0.0))
657767

658768
def test_getnewargs(self):
659769
self.assertEqual((1+2j).__getnewargs__(), (1.0, 2.0))
660770
self.assertEqual((1-2j).__getnewargs__(), (1.0, -2.0))
661-
self.assertEqual((2j).__getnewargs__(), (0.0, 2.0))
662-
self.assertEqual((-0j).__getnewargs__(), (0.0, -0.0))
771+
self.assertEqual((0.0+2j).__getnewargs__(), (0.0, 2.0))
772+
self.assertEqual((2j).__getnewargs__(), (2.0,))
773+
self.assertEqual((0.0-0j).__getnewargs__(), (0.0, -0.0))
774+
self.assertEqual((-0j).__getnewargs__(), (-0.0,))
663775
self.assertEqual(complex(0, INF).__getnewargs__(), (0.0, INF))
664776
self.assertEqual(complex(INF, 0).__getnewargs__(), (INF, 0.0))
665777

@@ -675,15 +787,11 @@ def test_negated_imaginary_literal(self):
675787
z0 = -0j
676788
z1 = -7j
677789
z2 = -1e1000j
678-
# Note: In versions of Python < 3.2, a negated imaginary literal
679-
# accidentally ended up with real part 0.0 instead of -0.0, thanks to a
680-
# modification during CST -> AST translation (see issue #9011). That's
681-
# fixed in Python 3.2.
682-
self.assertFloatsAreIdentical(z0.real, -0.0)
790+
self.assertFloatsAreIdentical(z0.real, +0.0)
683791
self.assertFloatsAreIdentical(z0.imag, -0.0)
684-
self.assertFloatsAreIdentical(z1.real, -0.0)
792+
self.assertFloatsAreIdentical(z1.real, +0.0)
685793
self.assertFloatsAreIdentical(z1.imag, -7.0)
686-
self.assertFloatsAreIdentical(z2.real, -0.0)
794+
self.assertFloatsAreIdentical(z2.real, +0.0)
687795
self.assertFloatsAreIdentical(z2.imag, -INF)
688796

689797
@support.requires_IEEE_754

Lib/test/test_fractions.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1610,7 +1610,7 @@ def test_complex_handling(self):
16101610
# See issue gh-102840 for more details.
16111611

16121612
a = F(1, 2)
1613-
b = 1j
1613+
b = 0.0+1j
16141614
message = "unsupported operand type(s) for %s: '%s' and '%s'"
16151615
# test forward
16161616
self.assertRaisesMessage(TypeError,

Lib/test/test_socket.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -931,7 +931,7 @@ def testSendtoErrors(self):
931931
self.assertEqual(str(cm.exception),
932932
"a bytes-like object is required, not 'str'")
933933
with self.assertRaises(TypeError) as cm:
934-
s.sendto(5j, sockname)
934+
s.sendto(1+5j, sockname)
935935
self.assertEqual(str(cm.exception),
936936
"a bytes-like object is required, not 'complex'")
937937
with self.assertRaises(TypeError) as cm:
@@ -943,7 +943,7 @@ def testSendtoErrors(self):
943943
self.assertEqual(str(cm.exception),
944944
"a bytes-like object is required, not 'str'")
945945
with self.assertRaises(TypeError) as cm:
946-
s.sendto(5j, 0, sockname)
946+
s.sendto(1+5j, 0, sockname)
947947
self.assertEqual(str(cm.exception),
948948
"a bytes-like object is required, not 'complex'")
949949
with self.assertRaises(TypeError) as cm:

Lib/test/test_str.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -1572,12 +1572,12 @@ def __int__(self):
15721572
self.assertRaisesRegex(TypeError, '%X format: an integer is required, not float', operator.mod, '%X', 2.11)
15731573
self.assertRaisesRegex(TypeError, '%o format: an integer is required, not float', operator.mod, '%o', 1.79)
15741574
self.assertRaisesRegex(TypeError, '%x format: an integer is required, not PseudoFloat', operator.mod, '%x', pi)
1575-
self.assertRaisesRegex(TypeError, '%x format: an integer is required, not complex', operator.mod, '%x', 3j)
1576-
self.assertRaisesRegex(TypeError, '%X format: an integer is required, not complex', operator.mod, '%X', 2j)
1577-
self.assertRaisesRegex(TypeError, '%o format: an integer is required, not complex', operator.mod, '%o', 1j)
1578-
self.assertRaisesRegex(TypeError, '%u format: a real number is required, not complex', operator.mod, '%u', 3j)
1579-
self.assertRaisesRegex(TypeError, '%i format: a real number is required, not complex', operator.mod, '%i', 2j)
1580-
self.assertRaisesRegex(TypeError, '%d format: a real number is required, not complex', operator.mod, '%d', 1j)
1575+
self.assertRaisesRegex(TypeError, '%x format: an integer is required, not complex', operator.mod, '%x', 1+3j)
1576+
self.assertRaisesRegex(TypeError, '%X format: an integer is required, not complex', operator.mod, '%X', 1+2j)
1577+
self.assertRaisesRegex(TypeError, '%o format: an integer is required, not complex', operator.mod, '%o', 1+1j)
1578+
self.assertRaisesRegex(TypeError, '%u format: a real number is required, not complex', operator.mod, '%u', 1+3j)
1579+
self.assertRaisesRegex(TypeError, '%i format: a real number is required, not complex', operator.mod, '%i', 1+2j)
1580+
self.assertRaisesRegex(TypeError, '%d format: a real number is required, not complex', operator.mod, '%d', 1+1j)
15811581
self.assertRaisesRegex(TypeError, '%c requires int or char', operator.mod, '%c', pi)
15821582

15831583
class RaisingNumber:

Modules/cmathmodule.c

+2-4
Original file line numberDiff line numberDiff line change
@@ -1231,15 +1231,13 @@ cmath_exec(PyObject *mod)
12311231
return -1;
12321232
}
12331233

1234-
Py_complex infj = {0.0, Py_INFINITY};
1235-
if (PyModule_Add(mod, "infj", PyComplex_FromCComplex(infj)) < 0) {
1234+
if (PyModule_Add(mod, "infj", PyImaginary_FromDouble(Py_INFINITY)) < 0) {
12361235
return -1;
12371236
}
12381237
if (PyModule_Add(mod, "nan", PyFloat_FromDouble(fabs(Py_NAN))) < 0) {
12391238
return -1;
12401239
}
1241-
Py_complex nanj = {0.0, fabs(Py_NAN)};
1242-
if (PyModule_Add(mod, "nanj", PyComplex_FromCComplex(nanj)) < 0) {
1240+
if (PyModule_Add(mod, "nanj", PyImaginary_FromDouble(Py_NAN)) < 0) {
12431241
return -1;
12441242
}
12451243

0 commit comments

Comments
 (0)