diff options
-rw-r--r-- | Lib/test/test_complex.py | 25 | ||||
-rw-r--r-- | Misc/NEWS | 13 | ||||
-rw-r--r-- | Objects/complexobject.c | 52 |
3 files changed, 81 insertions, 9 deletions
diff --git a/Lib/test/test_complex.py b/Lib/test/test_complex.py index 6441208..f6c7cc3 100644 --- a/Lib/test/test_complex.py +++ b/Lib/test/test_complex.py @@ -110,12 +110,18 @@ class ComplexTest(unittest.TestCase): self.assertRaises(TypeError, complex.__floordiv__, 3+0j, 0+0j) def test_richcompare(self): - self.assertRaises(OverflowError, complex.__eq__, 1+1j, 1<<10000) + self.assertIs(complex.__eq__(1+1j, 1<<10000), False) self.assertIs(complex.__lt__(1+1j, None), NotImplemented) self.assertIs(complex.__eq__(1+1j, 1+1j), True) self.assertIs(complex.__eq__(1+1j, 2+2j), False) self.assertIs(complex.__ne__(1+1j, 1+1j), False) self.assertIs(complex.__ne__(1+1j, 2+2j), True) + for i in range(1, 100): + f = i / 100.0 + self.assertIs(complex.__eq__(f+0j, f), True) + self.assertIs(complex.__ne__(f+0j, f), False) + self.assertIs(complex.__eq__(complex(f, f), f), False) + self.assertIs(complex.__ne__(complex(f, f), f), True) self.assertIs(complex.__lt__(1+1j, 2+2j), NotImplemented) self.assertIs(complex.__le__(1+1j, 2+2j), NotImplemented) self.assertIs(complex.__gt__(1+1j, 2+2j), NotImplemented) @@ -129,6 +135,23 @@ class ComplexTest(unittest.TestCase): self.assertIs(operator.ne(1+1j, 1+1j), False) self.assertIs(operator.ne(1+1j, 2+2j), True) + def test_richcompare_boundaries(self): + def check(n, deltas, is_equal, imag = 0.0): + for delta in deltas: + i = n + delta + z = complex(i, imag) + self.assertIs(complex.__eq__(z, i), is_equal(delta)) + self.assertIs(complex.__ne__(z, i), not is_equal(delta)) + # For IEEE-754 doubles the following should hold: + # x in [2 ** (52 + i), 2 ** (53 + i + 1)] -> x mod 2 ** i == 0 + # where the interval is representable, of course. + for i in range(1, 10): + pow = 52 + i + mult = 2 ** i + check(2 ** pow, range(1, 101), lambda delta: delta % mult == 0) + check(2 ** pow, range(1, 101), lambda delta: False, float(i)) + check(2 ** 53, range(-100, 0), lambda delta: True) + def test_mod(self): # % is no longer supported on complex numbers self.assertRaises(TypeError, (1+1j).__mod__, 0+0j) @@ -12,6 +12,19 @@ What's New in Python 3.2 Alpha 1? Core and Builtins ----------------- +- Issue #8748: Fix two issues with comparisons between complex and integer + objects. (1) The comparison could incorrectly return True in some cases + (2**53+1 == complex(2**53) == 2**53), breaking transivity of equality. + (2) The comparison raised an OverflowError for large integers, leading + to unpredictable exceptions when combining integers and complex objects + in sets or dicts. + +- Issue #8748: Fix comparisons between complex and integer objects. + These used to convert the integer object to a complex number before + doing the comparison, giving a potentially incorrect result when + that conversion involved precision loss. (Ex: 2**53+1 == + complex(2**53) returned True; now returns False.) + - Issue #8766: Initialize _warnings module before importing the first module. Fix a crash if an empty directory called "encodings" exists in sys.path. diff --git a/Objects/complexobject.c b/Objects/complexobject.c index ec26e0a..9e1e217 100644 --- a/Objects/complexobject.c +++ b/Objects/complexobject.c @@ -620,22 +620,58 @@ static PyObject * complex_richcompare(PyObject *v, PyObject *w, int op) { PyObject *res; - Py_complex i, j; - TO_COMPLEX(v, i); - TO_COMPLEX(w, j); + Py_complex i; + int equal; if (op != Py_EQ && op != Py_NE) { - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; + goto Unimplemented; } - if ((i.real == j.real && i.imag == j.imag) == (op == Py_EQ)) - res = Py_True; + assert(PyComplex_Check(v)); + TO_COMPLEX(v, i); + + if (PyLong_Check(w)) { + /* Check for 0.0 imaginary part first to avoid the rich + * comparison when possible. + */ + if (i.imag == 0.0) { + PyObject *j, *sub_res; + j = PyFloat_FromDouble(i.real); + if (j == NULL) + return NULL; + + sub_res = PyObject_RichCompare(j, w, op); + Py_DECREF(j); + return sub_res; + } + else { + equal = 0; + } + } + else if (PyFloat_Check(w)) { + equal = (i.real == PyFloat_AsDouble(w) && i.imag == 0.0); + } + else if (PyComplex_Check(w)) { + Py_complex j; + + TO_COMPLEX(w, j); + equal = (i.real == j.real && i.imag == j.imag); + } + else { + goto Unimplemented; + } + + if (equal == (op == Py_EQ)) + res = Py_True; else - res = Py_False; + res = Py_False; Py_INCREF(res); return res; + +Unimplemented: + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; } static PyObject * |