Skip to content

Commit ad476da

Browse files
committed
Issue #5816: Simplify code for parsing and printing of complex numbers.
nans and infs are no longer given special treatment; as a result, repr(complex(z)) recovers z for any complex number z.
1 parent f16e71d commit ad476da

4 files changed

Lines changed: 173 additions & 236 deletions

File tree

‎Lib/test/test_complex.py‎

Lines changed: 54 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from test import support
33

44
from random import random
5-
from math import atan2
5+
from math import atan2, isnan, copysign
66

77
INF = float("inf")
88
NAN = float("nan")
@@ -37,6 +37,29 @@ def assertCloseAbs(self, x, y, eps=1e-9):
3737
# check that relative difference < eps
3838
self.assert_(abs((x-y)/y) < eps)
3939

40+
def assertFloatsAreIdentical(self, x, y):
41+
"""assert that floats x and y are identical, in the sense that:
42+
(1) both x and y are nans, or
43+
(2) both x and y are infinities, with the same sign, or
44+
(3) both x and y are zeros, with the same sign, or
45+
(4) x and y are both finite and nonzero, and x == y
46+
47+
"""
48+
msg = 'floats {!r} and {!r} are not identical'
49+
50+
if isnan(x) or isnan(y):
51+
if isnan(x) and isnan(y):
52+
return
53+
elif x == y:
54+
if x != 0.0:
55+
return
56+
# both zero; check that signs match
57+
elif copysign(1.0, x) == copysign(1.0, y):
58+
return
59+
else:
60+
msg += ': zeros have different signs'
61+
self.fail(msg.format(x, y))
62+
4063
def assertClose(self, x, y, eps=1e-9):
4164
"""Return true iff complexes x and y "are close\""""
4265
self.assertCloseAbs(x.real, y.real, eps)
@@ -202,6 +225,8 @@ def __complex__(self): return self.value
202225
self.assertAlmostEqual(complex("+1"), +1)
203226
self.assertAlmostEqual(complex("(1+2j)"), 1+2j)
204227
self.assertAlmostEqual(complex("(1.3+2.2j)"), 1.3+2.2j)
228+
self.assertAlmostEqual(complex("3.14+1J"), 3.14+1j)
229+
self.assertAlmostEqual(complex(" ( +3.14-6J )"), 3.14-6j)
205230

206231
class complex2(complex): pass
207232
self.assertAlmostEqual(complex(complex2(1+1j)), 1+1j)
@@ -229,8 +254,6 @@ def split_zeros(x):
229254
self.assertRaises(TypeError, complex, "1", "1")
230255
self.assertRaises(TypeError, complex, 1, "1")
231256

232-
self.assertEqual(complex(" 3.14+J "), 3.14+1j)
233-
234257
# SF bug 543840: complex(string) accepts strings with \0
235258
# Fixed in 2.3.
236259
self.assertRaises(ValueError, complex, '1+1j\0j')
@@ -254,6 +277,11 @@ def split_zeros(x):
254277
self.assertRaises(ValueError, complex, "(1+2j)123")
255278
self.assertRaises(ValueError, complex, "1"*500)
256279
self.assertRaises(ValueError, complex, "x")
280+
self.assertRaises(ValueError, complex, "J")
281+
self.assertRaises(ValueError, complex, "1j+2")
282+
self.assertRaises(ValueError, complex, "1e1ej")
283+
self.assertRaises(ValueError, complex, "1e++1ej")
284+
self.assertRaises(ValueError, complex, ")1+2j(")
257285

258286
class EvilExc(Exception):
259287
pass
@@ -318,17 +346,17 @@ def test_repr(self):
318346
self.assertEqual(-6j,complex(repr(-6j)))
319347
self.assertEqual(6j,complex(repr(6j)))
320348

321-
self.assertEqual(repr(complex(1., INF)), "(1+inf*j)")
322-
self.assertEqual(repr(complex(1., -INF)), "(1-inf*j)")
349+
self.assertEqual(repr(complex(1., INF)), "(1+infj)")
350+
self.assertEqual(repr(complex(1., -INF)), "(1-infj)")
323351
self.assertEqual(repr(complex(INF, 1)), "(inf+1j)")
324-
self.assertEqual(repr(complex(-INF, INF)), "(-inf+inf*j)")
352+
self.assertEqual(repr(complex(-INF, INF)), "(-inf+infj)")
325353
self.assertEqual(repr(complex(NAN, 1)), "(nan+1j)")
326-
self.assertEqual(repr(complex(1, NAN)), "(1+nan*j)")
327-
self.assertEqual(repr(complex(NAN, NAN)), "(nan+nan*j)")
354+
self.assertEqual(repr(complex(1, NAN)), "(1+nanj)")
355+
self.assertEqual(repr(complex(NAN, NAN)), "(nan+nanj)")
328356

329-
self.assertEqual(repr(complex(0, INF)), "inf*j")
330-
self.assertEqual(repr(complex(0, -INF)), "-inf*j")
331-
self.assertEqual(repr(complex(0, NAN)), "nan*j")
357+
self.assertEqual(repr(complex(0, INF)), "infj")
358+
self.assertEqual(repr(complex(0, -INF)), "-infj")
359+
self.assertEqual(repr(complex(0, NAN)), "nanj")
332360

333361
def test_neg(self):
334362
self.assertEqual(-(1+6j), -1-6j)
@@ -367,6 +395,21 @@ def test_plus_minus_0j(self):
367395
self.assertEquals(atan2(z1.imag, -1.), atan2(0., -1.))
368396
self.assertEquals(atan2(z2.imag, -1.), atan2(-0., -1.))
369397

398+
@unittest.skipUnless(float.__getformat__("double").startswith("IEEE"),
399+
"test requires IEEE 754 doubles")
400+
def test_repr_roundtrip(self):
401+
# complex(repr(z)) should recover z exactly, even for complex numbers
402+
# involving an infinity, nan, or negative zero
403+
vals = [0.0, 1e-200, 0.0123, 3.1415, 1e50, INF, NAN]
404+
vals += [-v for v in vals]
405+
for x in vals:
406+
for y in vals:
407+
z = complex(x, y)
408+
roundtrip = complex(repr(z))
409+
self.assertFloatsAreIdentical(z.real, roundtrip.real)
410+
self.assertFloatsAreIdentical(z.imag, roundtrip.imag)
411+
412+
370413
def test_main():
371414
support.run_unittest(ComplexTest)
372415

‎Misc/NEWS‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ What's New in Python 3.1 beta 1?
1212
Core and Builtins
1313
-----------------
1414

15+
- Issue #5816: complex(repr(z)) now recovers z exactly, even when
16+
z involves nans, infs or negative zeros.
17+
1518
- Issue #3166: Make long -> float (and int -> float) conversions
1619
correctly rounded.
1720

0 commit comments

Comments
 (0)