From 9fd2ba8367e31195d63487c001b37fd637ea4a07 Mon Sep 17 00:00:00 2001 From: Luan Taraschi <130802253+luantaraschi@users.noreply.github.com> Date: Sat, 17 Oct 2026 23:01:54 -0300 Subject: [PATCH] Compare complex with int exactly instead of through a float (#9180) complex.__eq__ and __ne__ converted an int operand to f64 before comparing, so a complex whose real part is the float nearest to a large int compared equal to that int even when the values differ: complex(2**53 + 1) == 2**53 + 1 returned True, while float(2**53 + 1) == 2**53 + 1 returns False and the two objects hash differently. When the imaginary part is zero, compare the real part against the exact integer with float_ops::eq_int, the helper float's own comparison uses. This matches CPython, which hands an int operand to float's rich comparison. test_complex.test_richcompare_boundaries now passes, so drop its expectedFailure marker. Assisted-by: Claude Code:claude-opus-5-5 --- Lib/test/test_complex.py | 1 - crates/vm/src/builtins/complex.rs | 21 ++++++++++++--------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/Lib/test/test_complex.py b/Lib/test/test_complex.py index 6dc27ff07..91b37a8fb 100644 --- a/Lib/test/test_complex.py +++ b/Lib/test/test_complex.py @@ -244,7 +244,6 @@ class ComplexTest(ComplexesAreIdenticalMixin, unittest.TestCase): self.assertIs(operator.ne(1+1j, 2+2j), True) self.assertIs(operator.eq(1+1j, 2.0), False) - @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: True is not False def test_richcompare_boundaries(self): def check(n, deltas, is_equal, imag = 0.0): for delta in deltas: diff --git a/crates/vm/src/builtins/complex.rs b/crates/vm/src/builtins/complex.rs index eea71f475..40b38cb47 100644 --- a/crates/vm/src/builtins/complex.rs +++ b/crates/vm/src/builtins/complex.rs @@ -1,9 +1,9 @@ -use super::{PyStr, PyType, PyTypeRef, float}; +use super::{PyInt, PyStr, PyType, PyTypeRef, float}; use crate::{ AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, builtins::PyUtf8StrRef, class::{PyClassDef, PyClassImpl}, - common::wtf8::Wtf8Buf, + common::{float_ops, wtf8::Wtf8Buf}, convert::{ToPyObject, ToPyResult}, function::{FuncArgs, OptionalArg, PyComparisonValue}, protocol::PyNumberMethods, @@ -623,17 +623,20 @@ impl Comparable for PyComplex { zelf: &Py, other: &PyObject, op: PyComparisonOp, - vm: &VirtualMachine, + _vm: &VirtualMachine, ) -> PyResult { op.eq_only(|| { + let value = zelf.as_complex(); let result = if let Some(other) = other.downcast_ref::() { - zelf.as_complex() == other.as_complex() + value == other.as_complex() + } else if let Some(other) = other.downcast_ref::() { + // Compare with the exact integer, as float does, instead of + // rounding the integer to the nearest float first. + value.im == 0.0 && float_ops::eq_int(value.re, other.as_bigint()) + } else if let Some(other) = other.downcast_ref::() { + value == other.to_f64().into() } else { - match float::to_op_float(other, vm) { - Ok(Some(other)) => zelf.as_complex() == other.into(), - Err(_) => false, - Ok(None) => return Ok(PyComparisonValue::NotImplemented), - } + return Ok(PyComparisonValue::NotImplemented); }; Ok(PyComparisonValue::Implemented(result)) })