Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit cc6a982

Browse files
committed
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.
1 parent f0feec2 commit cc6a982

3 files changed

Lines changed: 81 additions & 9 deletions

File tree

Lib/test/test_complex.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,12 +110,18 @@ def test_floordiv(self):
110110
self.assertRaises(TypeError, complex.__floordiv__, 3+0j, 0+0j)
111111

112112
def test_richcompare(self):
113-
self.assertRaises(OverflowError, complex.__eq__, 1+1j, 1<<10000)
113+
self.assertIs(complex.__eq__(1+1j, 1<<10000), False)
114114
self.assertIs(complex.__lt__(1+1j, None), NotImplemented)
115115
self.assertIs(complex.__eq__(1+1j, 1+1j), True)
116116
self.assertIs(complex.__eq__(1+1j, 2+2j), False)
117117
self.assertIs(complex.__ne__(1+1j, 1+1j), False)
118118
self.assertIs(complex.__ne__(1+1j, 2+2j), True)
119+
for i in range(1, 100):
120+
f = i / 100.0
121+
self.assertIs(complex.__eq__(f+0j, f), True)
122+
self.assertIs(complex.__ne__(f+0j, f), False)
123+
self.assertIs(complex.__eq__(complex(f, f), f), False)
124+
self.assertIs(complex.__ne__(complex(f, f), f), True)
119125
self.assertIs(complex.__lt__(1+1j, 2+2j), NotImplemented)
120126
self.assertIs(complex.__le__(1+1j, 2+2j), NotImplemented)
121127
self.assertIs(complex.__gt__(1+1j, 2+2j), NotImplemented)
@@ -129,6 +135,23 @@ def test_richcompare(self):
129135
self.assertIs(operator.ne(1+1j, 1+1j), False)
130136
self.assertIs(operator.ne(1+1j, 2+2j), True)
131137

138+
def test_richcompare_boundaries(self):
139+
def check(n, deltas, is_equal, imag = 0.0):
140+
for delta in deltas:
141+
i = n + delta
142+
z = complex(i, imag)
143+
self.assertIs(complex.__eq__(z, i), is_equal(delta))
144+
self.assertIs(complex.__ne__(z, i), not is_equal(delta))
145+
# For IEEE-754 doubles the following should hold:
146+
# x in [2 ** (52 + i), 2 ** (53 + i + 1)] -> x mod 2 ** i == 0
147+
# where the interval is representable, of course.
148+
for i in range(1, 10):
149+
pow = 52 + i
150+
mult = 2 ** i
151+
check(2 ** pow, range(1, 101), lambda delta: delta % mult == 0)
152+
check(2 ** pow, range(1, 101), lambda delta: False, float(i))
153+
check(2 ** 53, range(-100, 0), lambda delta: True)
154+
132155
def test_mod(self):
133156
# % is no longer supported on complex numbers
134157
self.assertRaises(TypeError, (1+1j).__mod__, 0+0j)

Misc/NEWS

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,19 @@ What's New in Python 3.2 Alpha 1?
1212
Core and Builtins
1313
-----------------
1414

15+
- Issue #8748: Fix two issues with comparisons between complex and integer
16+
objects. (1) The comparison could incorrectly return True in some cases
17+
(2**53+1 == complex(2**53) == 2**53), breaking transivity of equality.
18+
(2) The comparison raised an OverflowError for large integers, leading
19+
to unpredictable exceptions when combining integers and complex objects
20+
in sets or dicts.
21+
22+
- Issue #8748: Fix comparisons between complex and integer objects.
23+
These used to convert the integer object to a complex number before
24+
doing the comparison, giving a potentially incorrect result when
25+
that conversion involved precision loss. (Ex: 2**53+1 ==
26+
complex(2**53) returned True; now returns False.)
27+
1528
- Issue #8766: Initialize _warnings module before importing the first module.
1629
Fix a crash if an empty directory called "encodings" exists in sys.path.
1730

Objects/complexobject.c

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -620,22 +620,58 @@ static PyObject *
620620
complex_richcompare(PyObject *v, PyObject *w, int op)
621621
{
622622
PyObject *res;
623-
Py_complex i, j;
624-
TO_COMPLEX(v, i);
625-
TO_COMPLEX(w, j);
623+
Py_complex i;
624+
int equal;
626625

627626
if (op != Py_EQ && op != Py_NE) {
628-
Py_INCREF(Py_NotImplemented);
629-
return Py_NotImplemented;
627+
goto Unimplemented;
630628
}
631629

632-
if ((i.real == j.real && i.imag == j.imag) == (op == Py_EQ))
633-
res = Py_True;
630+
assert(PyComplex_Check(v));
631+
TO_COMPLEX(v, i);
632+
633+
if (PyLong_Check(w)) {
634+
/* Check for 0.0 imaginary part first to avoid the rich
635+
* comparison when possible.
636+
*/
637+
if (i.imag == 0.0) {
638+
PyObject *j, *sub_res;
639+
j = PyFloat_FromDouble(i.real);
640+
if (j == NULL)
641+
return NULL;
642+
643+
sub_res = PyObject_RichCompare(j, w, op);
644+
Py_DECREF(j);
645+
return sub_res;
646+
}
647+
else {
648+
equal = 0;
649+
}
650+
}
651+
else if (PyFloat_Check(w)) {
652+
equal = (i.real == PyFloat_AsDouble(w) && i.imag == 0.0);
653+
}
654+
else if (PyComplex_Check(w)) {
655+
Py_complex j;
656+
657+
TO_COMPLEX(w, j);
658+
equal = (i.real == j.real && i.imag == j.imag);
659+
}
660+
else {
661+
goto Unimplemented;
662+
}
663+
664+
if (equal == (op == Py_EQ))
665+
res = Py_True;
634666
else
635-
res = Py_False;
667+
res = Py_False;
636668

637669
Py_INCREF(res);
638670
return res;
671+
672+
Unimplemented:
673+
Py_INCREF(Py_NotImplemented);
674+
return Py_NotImplemented;
639675
}
640676

641677
static PyObject *

0 commit comments

Comments
 (0)