From 4ddd89780fdb823f427c743ea7326a3c958a2f4b Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Mon, 20 Feb 2017 20:28:15 +0000 Subject: [PATCH 1/2] bpo-29602: fix signed zero handling in complex constructor --- Lib/test/test_complex.py | 29 +++++++++++++++++++++++++++++ Misc/NEWS | 4 ++++ Objects/complexobject.c | 6 +++--- 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_complex.py b/Lib/test/test_complex.py index c0383b27e66c03..6a82e06e12e283 100644 --- a/Lib/test/test_complex.py +++ b/Lib/test/test_complex.py @@ -8,6 +8,12 @@ NAN = float("nan") # These tests ensure that complex math does the right thing +# decorator for skipping tests on non-IEEE 754 platforms +requires_IEEE_754 = unittest.skipUnless(have_getformat and + float.__getformat__("double").startswith("IEEE"), + "test requires IEEE 754 doubles") + + class ComplexTest(unittest.TestCase): def assertAlmostEqual(self, a, b): @@ -441,6 +447,29 @@ def __rmod__(self,other): b = 'y %s x' % op self.assertTrue(type(eval(a)) is type(eval(b)) is xcomplex) + @requires_IEEE_754 + def test_constructor_special_numbers(self): + class complex2(complex): + pass + for x in 0.0, -0.0, INF, -INF, NAN: + for y in 0.0, -0.0, INF, -INF, NAN: + with self.subTest(x=x, y=y): + z = complex(x, y) + self.assertFloatsAreIdentical(z.real, x) + self.assertFloatsAreIdentical(z.imag, y) + z = complex2(x, y) + self.assertIs(type(z), complex2) + self.assertFloatsAreIdentical(z.real, x) + self.assertFloatsAreIdentical(z.imag, y) + z = complex(complex2(x, y)) + self.assertIs(type(z), complex) + self.assertFloatsAreIdentical(z.real, x) + self.assertFloatsAreIdentical(z.imag, y) + z = complex2(complex(x, y)) + self.assertIs(type(z), complex2) + self.assertFloatsAreIdentical(z.real, x) + self.assertFloatsAreIdentical(z.imag, y) + def test_hash(self): for x in xrange(-30, 30): self.assertEqual(hash(x), hash(complex(x, 0))) diff --git a/Misc/NEWS b/Misc/NEWS index 8726996a4bc18c..6551a6d34133c0 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,10 @@ What's New in Python 2.7.14? Core and Builtins ----------------- +- bpo-29602: Fix incorrect handling of signed zeros in complex constructor for + complex subclasses and for inputs having a __complex__ method. Patch + by Serhiy Storchaka. + - bpo-29347: Fixed possibly dereferencing undefined pointers when creating weakref objects. diff --git a/Objects/complexobject.c b/Objects/complexobject.c index ef18e3fea200e5..aaefa2dcda8a41 100644 --- a/Objects/complexobject.c +++ b/Objects/complexobject.c @@ -1232,11 +1232,11 @@ complex_new(PyTypeObject *type, PyObject *args, PyObject *kwds) return NULL; } cr.real = PyFloat_AsDouble(tmp); - cr.imag = 0.0; /* Shut up compiler warning */ + cr.imag = 0.0; Py_DECREF(tmp); } if (i == NULL) { - ci.real = 0.0; + ci.real = cr.imag; } else if (PyComplex_Check(i)) { ci = ((PyComplexObject*)i)->cval; @@ -1258,7 +1258,7 @@ complex_new(PyTypeObject *type, PyObject *args, PyObject *kwds) if (ci_is_complex) { cr.real -= ci.imag; } - if (cr_is_complex) { + if (cr_is_complex && i != NULL) { ci.real += cr.imag; } return complex_subtype_from_doubles(type, cr.real, ci.real); From 23e14d6763cec97c2fd6384df0820dfa2f4e461a Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Mon, 20 Feb 2017 20:53:12 +0000 Subject: [PATCH 2/2] Add missing have_getformat definition; remove use of unittest subtests. --- Lib/test/test_complex.py | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/Lib/test/test_complex.py b/Lib/test/test_complex.py index 6a82e06e12e283..02b292f4bbfe3f 100644 --- a/Lib/test/test_complex.py +++ b/Lib/test/test_complex.py @@ -9,6 +9,7 @@ # These tests ensure that complex math does the right thing # decorator for skipping tests on non-IEEE 754 platforms +have_getformat = hasattr(float, "__getformat__") requires_IEEE_754 = unittest.skipUnless(have_getformat and float.__getformat__("double").startswith("IEEE"), "test requires IEEE 754 doubles") @@ -453,22 +454,21 @@ class complex2(complex): pass for x in 0.0, -0.0, INF, -INF, NAN: for y in 0.0, -0.0, INF, -INF, NAN: - with self.subTest(x=x, y=y): - z = complex(x, y) - self.assertFloatsAreIdentical(z.real, x) - self.assertFloatsAreIdentical(z.imag, y) - z = complex2(x, y) - self.assertIs(type(z), complex2) - self.assertFloatsAreIdentical(z.real, x) - self.assertFloatsAreIdentical(z.imag, y) - z = complex(complex2(x, y)) - self.assertIs(type(z), complex) - self.assertFloatsAreIdentical(z.real, x) - self.assertFloatsAreIdentical(z.imag, y) - z = complex2(complex(x, y)) - self.assertIs(type(z), complex2) - self.assertFloatsAreIdentical(z.real, x) - self.assertFloatsAreIdentical(z.imag, y) + z = complex(x, y) + self.assertFloatsAreIdentical(z.real, x) + self.assertFloatsAreIdentical(z.imag, y) + z = complex2(x, y) + self.assertIs(type(z), complex2) + self.assertFloatsAreIdentical(z.real, x) + self.assertFloatsAreIdentical(z.imag, y) + z = complex(complex2(x, y)) + self.assertIs(type(z), complex) + self.assertFloatsAreIdentical(z.real, x) + self.assertFloatsAreIdentical(z.imag, y) + z = complex2(complex(x, y)) + self.assertIs(type(z), complex2) + self.assertFloatsAreIdentical(z.real, x) + self.assertFloatsAreIdentical(z.imag, y) def test_hash(self): for x in xrange(-30, 30):