From 9ddd07a65620660d75d521ae019d0fa49d67ed4e Mon Sep 17 00:00:00 2001 From: Lee Dogeon Date: Sun, 15 Mar 2026 17:04:23 +0900 Subject: [PATCH] Preserve imaginary zero signs when adding real values to complex numbers (#7421) * Preserve imaginary zero signs when adding real values to complex numbers * Refactor complex_add with match expression * Correct complex real subtract op * Remove unnecessary vm arugment --- Lib/test/test_builtin.py | 1 - crates/vm/src/builtins/complex.rs | 60 +++++++++++++++++++++++++++++-- 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index ff6175723..cf0268c2c 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -1971,7 +1971,6 @@ class BuiltinTest(ComplexesAreIdenticalMixin, unittest.TestCase): # test_str(): see test_str.py and test_bytes.py for str() tests. - @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: floats 0.0 and -0.0 are not identical: zeros have different signs def test_sum(self): self.assertEqual(sum([]), 0) self.assertEqual(sum(list(range(2,8))), 27) diff --git a/crates/vm/src/builtins/complex.rs b/crates/vm/src/builtins/complex.rs index b3425d2aa..3e905b6ec 100644 --- a/crates/vm/src/builtins/complex.rs +++ b/crates/vm/src/builtins/complex.rs @@ -287,6 +287,44 @@ impl PyComplex { Ok(vm.ctx.not_implemented()) } } + + fn complex_real_binop( + a: &PyObject, + b: &PyObject, + cc_op: CCF, + cr_op: CRF, + rc_op: RCF, + vm: &VirtualMachine, + ) -> PyResult + where + CCF: FnOnce(Complex64, Complex64) -> R, + CRF: FnOnce(Complex64, f64) -> R, + RCF: FnOnce(f64, Complex64) -> R, + R: ToPyResult, + { + let value = match (a.downcast_ref::(), b.downcast_ref::()) { + // complex + complex + (Some(a_complex), Some(b_complex)) => cc_op(a_complex.value, b_complex.value), + (Some(a_complex), None) => { + let Some(b_real) = float::to_op_float(b, vm)? else { + return Ok(vm.ctx.not_implemented()); + }; + + // complex + real + cr_op(a_complex.value, b_real) + } + (None, Some(b_complex)) => { + let Some(a_real) = float::to_op_float(a, vm)? else { + return Ok(vm.ctx.not_implemented()); + }; + + // real + complex + rc_op(a_real, b_complex.value) + } + (None, None) => return Ok(vm.ctx.not_implemented()), + }; + value.to_pyresult(vm) + } } #[pyclass( @@ -396,8 +434,26 @@ impl Hashable for PyComplex { impl AsNumber for PyComplex { fn as_number() -> &'static PyNumberMethods { static AS_NUMBER: PyNumberMethods = PyNumberMethods { - add: Some(|a, b, vm| PyComplex::number_op(a, b, |a, b, _vm| a + b, vm)), - subtract: Some(|a, b, vm| PyComplex::number_op(a, b, |a, b, _vm| a - b, vm)), + add: Some(|a, b, vm| { + PyComplex::complex_real_binop( + a, + b, + |a, b| a + b, + |a_complex, b_real| Complex64::new(a_complex.re + b_real, a_complex.im), + |a_real, b_complex| Complex64::new(a_real + b_complex.re, b_complex.im), + vm, + ) + }), + subtract: Some(|a, b, vm| { + PyComplex::complex_real_binop( + a, + b, + |a, b| a - b, + |a_complex, b_real| Complex64::new(a_complex.re - b_real, a_complex.im), + |a_real, b_complex| Complex64::new(a_real - b_complex.re, -b_complex.im), + vm, + ) + }), multiply: Some(|a, b, vm| PyComplex::number_op(a, b, |a, b, _vm| a * b, vm)), power: Some(|a, b, c, vm| { if vm.is_none(c) {