From db12274b1693d8ea32064c81f4983a6a4babdd80 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Fri, 17 May 2024 12:13:37 +0300 Subject: [PATCH] Print real part for complex even if it's zero * adapt Lib/test/test_format.py and Lib/test/test_complex.py * adjust doctests --- Doc/library/functions.rst | 4 ++-- Lib/test/test_complex.py | 27 +++++++++++++++++++-------- Lib/test/test_format.py | 13 +++++++++---- Objects/complexobject.c | 5 ++--- Python/formatter_unicode.c | 8 ++++++-- 5 files changed, 38 insertions(+), 19 deletions(-) diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index 0cd872e590a6ac..2ce70ccb9795db 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -389,7 +389,7 @@ are always available. They are listed here in alphabetical order. >>> complex('+1.23') (1.23+0j) >>> complex('-4.5j') - -4.5j + (0.0-4.5j) >>> complex('-1.23+4.5j') (-1.23+4.5j) >>> complex('\t( -1.23+4.5J )\n') @@ -399,7 +399,7 @@ are always available. They are listed here in alphabetical order. >>> complex(1.23) (1.23+0j) >>> complex(imag=-4.5) - -4.5j + (0.0-4.5j) >>> complex(-1.23, 4.5) (-1.23+4.5j) diff --git a/Lib/test/test_complex.py b/Lib/test/test_complex.py index 17c137cfdb7ab6..f8ac15d2fc4813 100644 --- a/Lib/test/test_complex.py +++ b/Lib/test/test_complex.py @@ -819,9 +819,13 @@ def test(v, expected, test_fn=self.assertEqual): test(complex(NAN, NAN), "(nan+nanj)") test(complex(-NAN, -NAN), "(nan+nanj)") - test(complex(0, INF), "infj") - test(complex(0, -INF), "-infj") - test(complex(0, NAN), "nanj") + test(complex(0, INF), "(0.0+infj)") + test(complex(0, -INF), "(0.0-infj)") + test(complex(0, NAN), "(0.0+nanj)") + + test(imaginary(INF), "infj") + test(imaginary(-INF), "-infj") + test(imaginary(NAN), "nanj") self.assertEqual(1-6j,complex(repr(1-6j))) self.assertEqual(1+6j,complex(repr(1+6j))) @@ -834,16 +838,22 @@ def test(v, expected, test_fn=self.assertEqual): test_fn(repr(v), expected) test_fn(str(v), expected) - test(complex(0., 1.), "1j") + test(complex(0., 1.), "(0.0+1j)") test(complex(-0., 1.), "(-0.0+1j)") - test(complex(0., -1.), "-1j") + test(complex(0., -1.), "(0.0-1j)") test(complex(-0., -1.), "(-0.0-1j)") - test(complex(0., 0.), "0j") - test(complex(0., -0.), "-0j") + test(imaginary(+1.), "1j") + test(imaginary(-1.), "-1j") + + test(complex(0., 0.), "(0.0+0j)") + test(complex(0., -0.), "(0.0-0j)") test(complex(-0., 0.), "(-0.0+0j)") test(complex(-0., -0.), "(-0.0-0j)") + test(imaginary(+0.0), "0j") + test(imaginary(-0.0), "-0j") + def test_pos(self): self.assertEqual(+(1+6j), 1+6j) self.assertEqual(+ComplexSubclass(1, 6), 1+6j) @@ -950,7 +960,8 @@ def test_format(self): self.assertEqual(format(z, '3'), str(z)) self.assertEqual(format(1+3j, 'g'), '1+3j') - self.assertEqual(format(3j, 'g'), '0+3j') + self.assertEqual(format(0+3j, 'g'), '0.0+3j') + self.assertEqual(format(3j, 'g'), '3j') self.assertEqual(format(1.5+3.5j, 'g'), '1.5+3.5j') self.assertEqual(format(1.5+3.5j, '+g'), '+1.5+3.5j') diff --git a/Lib/test/test_format.py b/Lib/test/test_format.py index 9dde63e40d06db..7d65dcd3e5cbe4 100644 --- a/Lib/test/test_format.py +++ b/Lib/test/test_format.py @@ -595,10 +595,15 @@ def test_negative_zero(self): self.assertEqual(f"{-1.:+z.0f}", "-1") self.assertEqual(f"{-1.:-z.0f}", "-1") - self.assertEqual(f"{0.j:z.1f}", "0.0+0.0j") - self.assertEqual(f"{-0.j:z.1f}", "0.0+0.0j") - self.assertEqual(f"{.01j:z.1f}", "0.0+0.0j") - self.assertEqual(f"{-.01j:z.1f}", "0.0+0.0j") + self.assertEqual(f"{0.0+0.j:z.1f}", "0.0+0.0j") + self.assertEqual(f"{0.0-0.j:z.1f}", "0.0+0.0j") + self.assertEqual(f"{0.0+.01j:z.1f}", "0.0+0.0j") + self.assertEqual(f"{0.0-.01j:z.1f}", "0.0+0.0j") + + self.assertEqual(f"{0.j:z.1f}", "0.0j") + self.assertEqual(f"{-0.j:z.1f}", "0.0j") + self.assertEqual(f"{.01j:z.1f}", "0.0j") + self.assertEqual(f"{-.01j:z.1f}", "0.0j") self.assertEqual(f"{-0.:z>6.1f}", "zz-0.0") # test fill, esp. 'z' fill self.assertEqual(f"{-0.:z>z6.1f}", "zzz0.0") diff --git a/Objects/complexobject.c b/Objects/complexobject.c index 905d638791f5e8..50b41cfcce3195 100644 --- a/Objects/complexobject.c +++ b/Objects/complexobject.c @@ -463,9 +463,8 @@ complex_repr(PyComplexObject *v) 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. */ + if (PyImaginary_Check(v)) { + /* Just output the imaginary part and do not include parens. */ re = ""; im = PyOS_double_to_string(v->cval.imag, format_code, precision, 0, NULL); diff --git a/Python/formatter_unicode.c b/Python/formatter_unicode.c index 27d59a033f27bc..d391fba88626ef 100644 --- a/Python/formatter_unicode.c +++ b/Python/formatter_unicode.c @@ -1271,7 +1271,7 @@ format_complex_internal(PyObject *value, /* Omitted type specifier. Should be like str(self). */ type = 'r'; default_precision = 0; - if (re == 0.0 && copysign(1.0, re) == 1.0) + if (PyImaginary_Check(value)) skip_re = 1; else add_parens = 1; @@ -1290,10 +1290,14 @@ 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. */ - if (re == 0.0 && copysign(1.0, re) == -1.0) + if (re == 0.0) { + if (PyImaginary_Check(value)) { + skip_re = 1; + } 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);