Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 21 additions & 10 deletions Lib/test/test_complex.py
Original file line number Diff line number Diff line change
Expand Up @@ -473,7 +473,7 @@ def test(v, expected, test_fn=self.assertEqual):
test(complex(NAN, NAN), "(nan+nanj)")

test(complex(0, INF), "infj")
test(complex(0, -INF), "-infj")
test(complex(0, -INF), "(0-infj)")
test(complex(0, NAN), "nanj")

self.assertEqual(1-6j,complex(repr(1-6j)))
Expand All @@ -487,15 +487,24 @@ def test(v, expected, test_fn=self.assertEqual):
test_fn(repr(v), expected)
test_fn(str(v), expected)

for x in 0.0, -0.0, 1.0, -1.0:
for y in 0.0, -0.0, 1.0, -1.0:
r = repr(complex(x, y))
with self.subTest(real=x, imag=y, repr=r):
z = eval(r)
self.assertEqual(z, complex(x, y))
self.assertFloatsAreIdentical(z.real, x)
self.assertFloatsAreIdentical(z.imag, y)

test(complex(0., 1.), "1j")
test(complex(-0., 1.), "(-0+1j)")
test(complex(0., -1.), "-1j")
test(complex(-0., -1.), "(-0-1j)")
test(complex(-0., 1.), "-(0-1j)")
test(complex(0., -1.), "(0-1j)")
test(complex(-0., -1.), "(-0.0-1j)")

test(complex(0., 0.), "0j")
test(complex(0., -0.), "-0j")
test(complex(-0., 0.), "(-0+0j)")
test(complex(-0., -0.), "(-0-0j)")
test(complex(0., -0.), "-(-0.0-0j)")
test(complex(-0., 0.), "(-0.0-0j)")
test(complex(-0., -0.), "-(0+0j)")

def test_neg(self):
self.assertEqual(-(1+6j), -1-6j)
Expand Down Expand Up @@ -563,9 +572,11 @@ def test_repr_roundtrip(self):
for x in vals:
for y in vals:
z = complex(x, y)
roundtrip = complex(repr(z))
self.assertFloatsAreIdentical(z.real, roundtrip.real)
self.assertFloatsAreIdentical(z.imag, roundtrip.imag)
r = repr(z)
with self.subTest(x=x, y=y, repr=r):
roundtrip = complex(r)
self.assertFloatsAreIdentical(z.real, roundtrip.real)
self.assertFloatsAreIdentical(z.imag, roundtrip.imag)

# if we predefine some constants, then eval(repr(z)) should
# also work, except that it might change the sign of zeros
Expand Down
106 changes: 79 additions & 27 deletions Objects/complexobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -353,8 +353,6 @@ PyComplex_AsCComplex(PyObject *op)
static PyObject *
complex_repr(PyComplexObject *v)
{
int precision = 0;
char format_code = 'r';
PyObject *result = NULL;

/* If these are non-NULL, they'll need to be freed. */
Expand All @@ -366,37 +364,80 @@ complex_repr(PyComplexObject *v)
are pointers to constants. */
const char *re = NULL;
const char *lead = "";
const char *tail = "";

if (v->cval.real == 0. && copysign(1.0, v->cval.real)==1.0) {
/* Real part is +0: just output the imaginary part and do not
include parens. */
const char *tail = ")";

if (v->cval.real == 0.0
&& copysign(1.0, v->cval.real) > 0.0
&& copysign(1.0, v->cval.imag) > 0.0)
{
/* Real part is +0, imaginary part is not negative: just output
the imaginary part and do not include parens. */
re = "";
im = PyOS_double_to_string(v->cval.imag, format_code,
precision, 0, NULL);
if (!im) {
PyErr_NoMemory();
goto done;
im = PyOS_double_to_string(v->cval.imag, 'r', 0, 0, NULL);
tail = "";
}
else
if (v->cval.real == 0.0
&& v->cval.imag == 0.0
&& copysign(1.0, v->cval.real) != copysign(1.0, v->cval.imag))
{
/* Real part is +0, imaginary part is -0 or
real part is -0, imaginary part is +0. */
re = "-0.0";
im = PyOS_double_to_string(-0.0, 'r', 0, Py_DTSF_SIGN, NULL);
lead = copysign(1.0, v->cval.real) > 0.0 ? "-(" : "(";
}
else
if (v->cval.real == 0.0
&& copysign(1.0, v->cval.real) < 0.0
&& copysign(1.0, v->cval.imag) > 0.0)
{
/* Real part is -0, imaginary part is not negative. */
re = "0";
im = PyOS_double_to_string(-v->cval.imag, 'r', 0, Py_DTSF_SIGN, NULL);
lead = "-(";
}
else
if (v->cval.imag == 0.0
&& copysign(1.0, v->cval.imag) < 0.0)
{
/* Imaginary part is -0. */
if (v->cval.real == 0.0 && copysign(1.0, v->cval.real) > 0.0) {
re = "-0.0";
}
} else {
else {
pre = PyOS_double_to_string(-v->cval.real, 'r', 0, 0, NULL);
if (!pre) {
PyErr_NoMemory();
goto done;
}
re = pre;
}

im = PyOS_double_to_string(-v->cval.imag, 'r', 0, Py_DTSF_SIGN, NULL);
lead = "-(";
}
else {
/* Format imaginary part with sign, real part without. Include
parens in the result. */
pre = PyOS_double_to_string(v->cval.real, format_code,
precision, 0, NULL);
if (!pre) {
PyErr_NoMemory();
goto done;
if (v->cval.real == 0.0 && copysign(1.0, v->cval.real) < 0.0) {
re = "-0.0";
}
re = pre;

im = PyOS_double_to_string(v->cval.imag, format_code,
precision, Py_DTSF_SIGN, NULL);
if (!im) {
PyErr_NoMemory();
goto done;
else {
pre = PyOS_double_to_string(v->cval.real, 'r', 0, 0, NULL);
if (!pre) {
PyErr_NoMemory();
goto done;
}
re = pre;
}

im = PyOS_double_to_string(v->cval.imag, 'r', 0, Py_DTSF_SIGN, NULL);
lead = "(";
tail = ")";
}
if (!im) {
PyErr_NoMemory();
goto done;
}
result = PyUnicode_FromFormat("%s%s%sj%s", lead, re, im, tail);
done:
Expand Down Expand Up @@ -760,9 +801,16 @@ complex_from_string_inner(const char *s, Py_ssize_t len, void *type)
start = s;
while (Py_ISSPACE(*s))
s++;

if (*s == '(') {
/* Skip over possible bracket from repr(). */
got_bracket = 1;
}
else if (*s == '-' && s[1] == '(') {
got_bracket = -1;
s++;
}
if (got_bracket) {
/* Skip over possible bracket from repr(). */
s++;
while (Py_ISSPACE(*s))
s++;
Expand Down Expand Up @@ -863,6 +911,10 @@ complex_from_string_inner(const char *s, Py_ssize_t len, void *type)
if (s-start != len)
goto parse_error;

if (got_bracket < 0) {
x = -x;
y = -y;
}
return complex_subtype_from_doubles((PyTypeObject *)type, x, y);

parse_error:
Expand Down