Issue #8748: Fix incorrect results from comparisons between an integer

and a complex instance.  Based on a patch by Meador Inge.
This commit is contained in:
Mark Dickinson 2010-05-30 13:18:10 +00:00
parent 4b3035d0b8
commit 4ca7c3c089
3 changed files with 80 additions and 11 deletions

View File

@ -129,7 +129,7 @@ class ComplexTest(unittest.TestCase):
self.assertTrue(a < 2.0j)
def test_richcompare(self):
self.assertRaises(OverflowError, complex.__eq__, 1+1j, 1L<<10000)
self.assertEqual(complex.__eq__(1+1j, 1L<<10000), False)
self.assertEqual(complex.__lt__(1+1j, None), NotImplemented)
self.assertIs(complex.__eq__(1+1j, 1+1j), True)
self.assertIs(complex.__eq__(1+1j, 2+2j), False)
@ -140,6 +140,23 @@ class ComplexTest(unittest.TestCase):
self.assertRaises(TypeError, complex.__gt__, 1+1j, 2+2j)
self.assertRaises(TypeError, complex.__ge__, 1+1j, 2+2j)
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):
self.assertRaises(ZeroDivisionError, (1+1j).__mod__, 0+0j)

View File

@ -12,6 +12,13 @@ What's New in Python 2.7 Release Candidate 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 #5211: Implicit coercion for the complex type is now completely
removed. (Coercion for arithmetic operations was already removed in 2.7
alpha 4, but coercion for rich comparisons was accidentally left in.)

View File

@ -783,25 +783,70 @@ complex_coerce(PyObject **pv, PyObject **pw)
static PyObject *
complex_richcompare(PyObject *v, PyObject *w, int op)
{
Py_complex i, j;
PyObject *res;
TO_COMPLEX(v, i);
TO_COMPLEX(w, j);
Py_complex i;
int equal;
if (op != Py_EQ && op != Py_NE) {
PyErr_SetString(PyExc_TypeError,
"no ordering relation is defined for complex numbers");
return NULL;
/* for backwards compatibility, comparisons with non-numbers return
* NotImplemented. Only comparisons with core numeric types raise
* TypeError.
*/
if (PyInt_Check(w) || PyLong_Check(w) ||
PyFloat_Check(w) || PyComplex_Check(w)) {
PyErr_SetString(PyExc_TypeError,
"no ordering relation is defined "
"for complex numbers");
return NULL;
}
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 (PyInt_Check(w) || 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 *