From 737293b3c607a715e865c8f58de61a361f41c731 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Sat, 11 May 2024 12:06:42 +0300 Subject: [PATCH] Use floats for signed zeros in the real part of repr(complex) >>> complex(-0.0, 1) # was (-0+1j) (-0.0+1j) This doesn't break complex(str(x)) == x invariant as we had support for parsing signed 0.0's here before. --- Lib/test/test_complex.py | 8 ++++---- Objects/complexobject.c | 4 ++-- Python/formatter_unicode.c | 9 +++++++-- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/Lib/test/test_complex.py b/Lib/test/test_complex.py index 3c1738c3160d1e..17c137cfdb7ab6 100644 --- a/Lib/test/test_complex.py +++ b/Lib/test/test_complex.py @@ -835,14 +835,14 @@ def test(v, expected, test_fn=self.assertEqual): test_fn(str(v), expected) test(complex(0., 1.), "1j") - test(complex(-0., 1.), "(-0+1j)") + test(complex(-0., 1.), "(-0.0+1j)") test(complex(0., -1.), "-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)") def test_pos(self): self.assertEqual(+(1+6j), 1+6j) diff --git a/Objects/complexobject.c b/Objects/complexobject.c index 6919d70eb1e0d6..905d638791f5e8 100644 --- a/Objects/complexobject.c +++ b/Objects/complexobject.c @@ -476,8 +476,8 @@ complex_repr(PyComplexObject *v) } 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); + pre = PyOS_double_to_string(v->cval.real, format_code, precision, + v->cval.real? 0 : Py_DTSF_ADD_DOT_0, NULL); if (!pre) { PyErr_NoMemory(); goto done; diff --git a/Python/formatter_unicode.c b/Python/formatter_unicode.c index ebd67214f43042..27d59a033f27bc 100644 --- a/Python/formatter_unicode.c +++ b/Python/formatter_unicode.c @@ -1290,8 +1290,13 @@ format_complex_internal(PyObject *value, /* Cast "type", because if we're in unicode we need to pass an 8-bit char. This is safe, because we've restricted what "type" can be. */ - re_buf = PyOS_double_to_string(re, (char)type, precision, flags, - &re_float_type); + if (re == 0.0 && copysign(1.0, re) == -1.0) + re_buf = PyOS_double_to_string(re, (char)type, precision, + flags | Py_DTSF_ADD_DOT_0, + &re_float_type); + else + re_buf = PyOS_double_to_string(re, (char)type, precision, flags, + &re_float_type); if (re_buf == NULL) goto done; im_buf = PyOS_double_to_string(im, (char)type, precision, flags,