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.

Patch by Meador Inge.
This commit is contained in:
Mark Dickinson 2010-05-21 14:55:26 +00:00
parent f0feec2cb6
commit cc6a982de8
3 changed files with 81 additions and 9 deletions

View File

@ -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)

View File

@ -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.

View File

@ -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 *