bpo-40269: Fix repr for complex.

eval(repr(z)) restores now the sign of zero components of z.
This commit is contained in:
Serhiy Storchaka 2018-11-06 10:55:13 +02:00
parent 482259d0dc
commit 18eac99151
2 changed files with 99 additions and 36 deletions

View File

@ -473,7 +473,7 @@ class ComplexTest(unittest.TestCase):
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)))
@ -487,15 +487,24 @@ class ComplexTest(unittest.TestCase):
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)
@ -563,9 +572,11 @@ class ComplexTest(unittest.TestCase):
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

View File

@ -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. */
@ -366,37 +364,80 @@ complex_repr(PyComplexObject *v)
are pointers to constants. */
const char *re = NULL;
const char *lead = "";
const char *tail = "";
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. */
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";
}
else {
pre = PyOS_double_to_string(v->cval.real, 'r', 0, 0, NULL);
if (!pre) {
PyErr_NoMemory();
goto done;
}
re = pre;
}
re = pre;
im = PyOS_double_to_string(v->cval.imag, format_code,
precision, Py_DTSF_SIGN, NULL);
if (!im) {
PyErr_NoMemory();
goto done;
}
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:
@ -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++;
@ -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: