rust-lang/rust · #162832

add `Div` and `Mul` for `Complex<{float}>`

folkertdev · merged Sep 29, 20264 files · 166 + / 1 −
compiler/rustc_codegen_cranelift/patches/0029-sysroot_tests-Disable-complex-type-tests.patchadded32 + / 0 −
@@ -0,0 +1,32 @@+From 20d296e9a8b837e586fcc8fc648d4ab9687a2a47 Mon Sep 17 00:00:00 2001+From: Folkert de Vries <folkert@folkertdev.nl>+Date: Mon, 28 Sep 2026 19:14:22 +0200+Subject: [PATCH] sysroot_tests: Disable complex type tests++---+ coretests/tests/num/complex.rs | 2 +++ 1 file changed, 2 insertions(+)++diff --git a/coretests/tests/num/complex.rs b/coretests/tests/num/complex.rs+index d73d04b1afc..289c0baec17 100644+--- a/coretests/tests/num/complex.rs++++ b/coretests/tests/num/complex.rs+@@ -75,6 +75,7 @@ fn complex_negation() {+ }+ + #[test]++#[cfg_attr(target_os = "windows", ignore = "hits an ABI issue with cranelift on windows")]+ fn complex_multiplication() {+     #[cfg(target_has_reliable_f16)]+     assert_eq!(Complex::new(1.0f16, 2.0) * Complex::new(3.0, 4.0), Complex::new(-5.0, 10.0));+@@ -105,6 +106,7 @@ fn complex_multiplication() {+ }+ + #[test]++#[cfg_attr(target_os = "windows", ignore = "hits an ABI issue with cranelift on windows")]+ fn complex_div() {+     #[cfg(target_has_reliable_f16)]+     assert_eq!(Complex::new(2.0f16, 11.0) / Complex::new(2.0, 1.0), Complex::new(3.0, 4.0));+-- +2.43.0+
library/core/src/num/complex.rs53 + / 1 −
@@ -1,4 +1,5 @@-use crate::ops::{Add, Neg, Sub};+use crate::num::imp::libm::complex::*;+use crate::ops::{Add, Div, Mul, Neg, Sub};  /// A complex number. #[derive(Clone, Copy, Debug, PartialEq, Eq)]@@ -91,3 +92,54 @@ impl<T: Sub<Output = T>> Sub<T> for Complex<T> {         Complex::new(self.re - rhs, self.im)     } }++macro_rules! impl_complex_mul_div {+    ($ty:ty, $mul:ident, $div:ident) => {+        #[unstable(feature = "complex_numbers", issue = "154023")]+        impl Mul for Complex<$ty> {+            type Output = Self;++            #[inline]+            fn mul(self, rhs: Self) -> Self::Output {+                let Complex { re: a, im: b } = self;+                let Complex { re: c, im: d } = rhs;++                let ac = a * c;+                let bd = b * d;+                let ad = a * d;+                let bc = b * c;++                let z = Complex::new(ac - bd, ad + bc);++                // Only call the libcall when both components are NaN.+                //+                // The naive algorithm would return NaN + NaNi for an input like+                // (1 + 0i) * (inf + infi). The libcall instead returns inf + infi.+                //+                // We duplicate the fast path here so that it can be inlined. We use a libcall+                // for the NaN correction to reduce the size of `core`.+                if z.re.is_nan() && z.im.is_nan() {+                    crate::hint::cold_path();+                    $mul(a, b, c, d)+                } else {+                    z+                }+            }+        }++        #[unstable(feature = "complex_numbers", issue = "154023")]+        impl Div for Complex<$ty> {+            type Output = Self;++            #[inline]+            fn div(self, rhs: Self) -> Self::Output {+                $div(self.re, self.im, rhs.re, rhs.im)+            }+        }+    };+}++impl_complex_mul_div!(f16, __rust_mulhc3, __rust_divhc3);+impl_complex_mul_div!(f32, __mulsc3, __divsc3);+impl_complex_mul_div!(f64, __muldc3, __divdc3);+impl_complex_mul_div!(f128, __rust_multc3, __rust_divtc3);
library/core/src/num/imp/libm.rs21 + / 0 −
@@ -76,6 +76,27 @@ unsafe extern "C" {     pub(crate) safe fn truncf16(x: f16) -> f16; } +/// These symbols are always provided by compiler-builtins.+pub(crate) mod complex {+    use crate::num::Complex;++    unsafe extern "C" {+        pub(crate) safe fn __mulsc3(a: f32, b: f32, c: f32, d: f32) -> Complex<f32>;+        pub(crate) safe fn __muldc3(a: f64, b: f64, c: f64, d: f64) -> Complex<f64>;++        pub(crate) safe fn __divsc3(a: f32, b: f32, c: f32, d: f32) -> Complex<f32>;+        pub(crate) safe fn __divdc3(a: f64, b: f64, c: f64, d: f64) -> Complex<f64>;+    }++    unsafe extern "Rust" {+        pub(crate) safe fn __rust_mulhc3(a: f16, b: f16, c: f16, d: f16) -> Complex<f16>;+        pub(crate) safe fn __rust_multc3(a: f128, b: f128, c: f128, d: f128) -> Complex<f128>;++        pub(crate) safe fn __rust_divhc3(a: f16, b: f16, c: f16, d: f16) -> Complex<f16>;+        pub(crate) safe fn __rust_divtc3(a: f128, b: f128, c: f128, d: f128) -> Complex<f128>;+    }+}+ /// These symbols will be available when `std` is available, and on many no-std platforms. However, /// since this isn't a guarantee, we cannot rely on them for stable implementations. pub(crate) mod likely_available {
library/coretests/tests/num/complex.rs60 + / 0 −
@@ -73,3 +73,63 @@ fn complex_negation() {     assert_eq!(-Complex::new(1.0, -2.0), Complex::new(-1.0, 2.0));     assert_eq!(-Complex::new(1.0, f32::INFINITY), Complex::new(-1.0, f32::NEG_INFINITY),); }++#[test]+fn complex_multiplication() {+    #[cfg(target_has_reliable_f16)]+    assert_eq!(Complex::new(1.0f16, 2.0) * Complex::new(3.0, 4.0), Complex::new(-5.0, 10.0));+    assert_eq!(Complex::new(1.0f32, 2.0) * Complex::new(3.0, 4.0), Complex::new(-5.0, 10.0));+    assert_eq!(Complex::new(1.0f64, 2.0) * Complex::new(3.0, 4.0), Complex::new(-5.0, 10.0));+    #[cfg(target_has_reliable_f128)]+    assert_eq!(Complex::new(1.0f128, 2.0) * Complex::new(3.0, 4.0), Complex::new(-5.0, 10.0));++    // The naive algorithm would return NaN + NaNi for these inputs, but the libcall handles it.+    #[cfg(target_has_reliable_f16)]+    assert_eq!(+        Complex::new(1.0, 0.0) * Complex::new(f16::INFINITY, f16::INFINITY),+        Complex::new(f16::INFINITY, f16::INFINITY)+    );+    assert_eq!(+        Complex::new(1.0, 0.0) * Complex::new(f32::INFINITY, f32::INFINITY),+        Complex::new(f32::INFINITY, f32::INFINITY)+    );+    assert_eq!(+        Complex::new(1.0, 0.0) * Complex::new(f64::INFINITY, f64::INFINITY),+        Complex::new(f64::INFINITY, f64::INFINITY)+    );+    #[cfg(target_has_reliable_f128)]+    assert_eq!(+        Complex::new(1.0, 0.0) * Complex::new(f128::INFINITY, f128::INFINITY),+        Complex::new(f128::INFINITY, f128::INFINITY)+    );+}++#[test]+fn complex_div() {+    #[cfg(target_has_reliable_f16)]+    assert_eq!(Complex::new(2.0f16, 11.0) / Complex::new(2.0, 1.0), Complex::new(3.0, 4.0));+    assert_eq!(Complex::new(2.0f32, 11.0) / Complex::new(2.0, 1.0), Complex::new(3.0, 4.0));+    assert_eq!(Complex::new(2.0f64, 11.0) / Complex::new(2.0, 1.0), Complex::new(3.0, 4.0));+    #[cfg(target_has_reliable_f128)]+    assert_eq!(Complex::new(2.0f128, 11.0) / Complex::new(2.0, 1.0), Complex::new(3.0, 4.0));++    // The naive algorithm would return NaN + NaNi for these inputs, but the libcall handles it.+    #[cfg(target_has_reliable_f16)]+    assert_eq!(+        Complex::new(f16::INFINITY, 0.0) / Complex::new(1.0, 1.0),+        Complex::new(f16::INFINITY, f16::NEG_INFINITY)+    );+    assert_eq!(+        Complex::new(f32::INFINITY, 0.0) / Complex::new(1.0, 1.0),+        Complex::new(f32::INFINITY, f32::NEG_INFINITY)+    );+    assert_eq!(+        Complex::new(f64::INFINITY, 0.0) / Complex::new(1.0, 1.0),+        Complex::new(f64::INFINITY, f64::NEG_INFINITY)+    );+    #[cfg(target_has_reliable_f128)]+    assert_eq!(+        Complex::new(f128::INFINITY, 0.0) / Complex::new(1.0, 1.0),+        Complex::new(f128::INFINITY, f128::NEG_INFINITY)+    );+}