From 66f66cc92daf5eb2462a4ef3d3d653c1afe29ffd Mon Sep 17 00:00:00 2001 From: Owen Kirchenstien Date: Wed, 28 Jan 2026 15:00:33 -0500 Subject: [PATCH 1/9] Add mul_add_relaxed methods for floating-point types Implements mul_add_relaxed for f16, f32, f64, and f128, which computes (self * a) + b with relaxed precision semantics. Unlike mul_add which guarantees a fused operation, this variant allows the compiler to choose between fused or separate operations based on target performance. This fills the gap between the precision-guaranteed mul_add and the fully-optimizable algebraic operators, providing target-specific optimization while maintaining reasonable floating-point semantics. Tracking issue: #151770 --- compiler/rustc_span/src/symbol.rs | 1 + library/core/src/num/f128.rs | 53 ++++++++++++++++++++ library/core/src/num/f16.rs | 53 ++++++++++++++++++++ library/core/src/num/f32.rs | 50 ++++++++++++++++++ library/core/src/num/f64.rs | 50 ++++++++++++++++++ library/std/src/num/f32.rs | 51 +++++++++++++++++++ library/std/src/num/f64.rs | 51 +++++++++++++++++++ tests/ui/intrinsics/float-mul-add-relaxed.rs | 49 ++++++++++++++++++ 8 files changed, 358 insertions(+) create mode 100644 tests/ui/intrinsics/float-mul-add-relaxed.rs diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 1ebada3c285e7..cb8da114f1a9c 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1089,6 +1089,7 @@ symbols! { file_options, flags, float, + float_mul_add_relaxed, float_to_int_unchecked, floorf16, floorf32, diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index cc142fab8e821..4d816bc10acfb 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -1449,6 +1449,59 @@ impl f128 { pub const fn algebraic_rem(self, rhs: f128) -> f128 { intrinsics::frem_algebraic(self, rhs) } + + /// Fused multiply-add with relaxed precision semantics. Computes `(self * a) + b`, + /// non-deterministically executing either a fused multiply-add (one rounding) or + /// two separate operations with intermediate rounding. + /// + /// The operation may be fused if the code generator determines that the target + /// instruction set has support for a fused operation and that it is more efficient + /// than separate multiply and add instructions. Whether fusion occurs is unspecified + /// and may depend on optimization level and context. + /// + /// # Precision + /// + /// Unlike [`mul_add`](Self::mul_add), this operation does not guarantee which + /// rounding behavior will occur. It may perform either: + /// - A fused multiply-add with one rounding (more accurate) + /// - Separate multiply and add operations with two roundings (less accurate) + /// + /// Use this method when you need performance optimization but can tolerate + /// non-deterministic precision. If you require guaranteed precision, use + /// [`mul_add`](Self::mul_add) (guaranteed one rounding) or the separate + /// multiply and add operations (guaranteed two roundings). + /// + /// If you want even more optimization opportunities and aren't concerned about + /// error bounds, consider using the algebraic operations such as + /// [`algebraic_mul`](Self::algebraic_mul) and [`algebraic_add`](Self::algebraic_add). + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// #![feature(float_mul_add_relaxed)] + /// # #[cfg(reliable_f128)] { + /// + /// let m = 10.0_f128; + /// let x = 4.0_f128; + /// let b = 60.0_f128; + /// + /// // The result may be computed as either: + /// // - (m * x) + b with fused rounding, or + /// // - (m * x) + b with separate roundings + /// let result = m.mul_add_relaxed(x, b); + /// + /// // For simple values, both approaches give the same result + /// assert_eq!(result, 100.0); + /// # } + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "float_mul_add_relaxed", issue = "151770")] + #[rustc_const_unstable(feature = "float_mul_add_relaxed", issue = "151770")] + #[inline] + pub const fn mul_add_relaxed(self, a: f128, b: f128) -> f128 { + intrinsics::fmuladdf128(self, a, b) + } } // Functions in this module fall into `core_float_math` diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index e97a44e991f66..2569799a04d05 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -1434,6 +1434,59 @@ impl f16 { pub const fn algebraic_rem(self, rhs: f16) -> f16 { intrinsics::frem_algebraic(self, rhs) } + + /// Fused multiply-add with relaxed precision semantics. Computes `(self * a) + b`, + /// non-deterministically executing either a fused multiply-add (one rounding) or + /// two separate operations with intermediate rounding. + /// + /// The operation may be fused if the code generator determines that the target + /// instruction set has support for a fused operation and that it is more efficient + /// than separate multiply and add instructions. Whether fusion occurs is unspecified + /// and may depend on optimization level and context. + /// + /// # Precision + /// + /// Unlike [`mul_add`](Self::mul_add), this operation does not guarantee which + /// rounding behavior will occur. It may perform either: + /// - A fused multiply-add with one rounding (more accurate) + /// - Separate multiply and add operations with two roundings (less accurate) + /// + /// Use this method when you need performance optimization but can tolerate + /// non-deterministic precision. If you require guaranteed precision, use + /// [`mul_add`](Self::mul_add) (guaranteed one rounding) or the separate + /// multiply and add operations (guaranteed two roundings). + /// + /// If you want even more optimization opportunities and aren't concerned about + /// error bounds, consider using the algebraic operations such as + /// [`algebraic_mul`](Self::algebraic_mul) and [`algebraic_add`](Self::algebraic_add). + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// #![feature(float_mul_add_relaxed)] + /// # #[cfg(reliable_f16)] { + /// + /// let m = 10.0_f16; + /// let x = 4.0_f16; + /// let b = 60.0_f16; + /// + /// // The result may be computed as either: + /// // - (m * x) + b with fused rounding, or + /// // - (m * x) + b with separate roundings + /// let result = m.mul_add_relaxed(x, b); + /// + /// // For simple values, both approaches give the same result + /// assert_eq!(result, 100.0); + /// # } + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "float_mul_add_relaxed", issue = "151770")] + #[rustc_const_unstable(feature = "float_mul_add_relaxed", issue = "151770")] + #[inline] + pub const fn mul_add_relaxed(self, a: f16, b: f16) -> f16 { + intrinsics::fmuladdf16(self, a, b) + } } // Functions in this module fall into `core_float_math` diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 3d8249631037f..553e8815ade41 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -1612,6 +1612,56 @@ impl f32 { pub const fn algebraic_rem(self, rhs: f32) -> f32 { intrinsics::frem_algebraic(self, rhs) } + + /// Fused multiply-add with relaxed precision semantics. Computes `(self * a) + b`, + /// non-deterministically executing either a fused multiply-add (one rounding) or + /// two separate operations with intermediate rounding. + /// + /// The operation may be fused if the code generator determines that the target + /// instruction set has support for a fused operation and that it is more efficient + /// than separate multiply and add instructions. Whether fusion occurs is unspecified + /// and may depend on optimization level and context. + /// + /// # Precision + /// + /// Unlike [`mul_add`](Self::mul_add), this operation does not guarantee which + /// rounding behavior will occur. It may perform either: + /// - A fused multiply-add with one rounding (more accurate) + /// - Separate multiply and add operations with two roundings (less accurate) + /// + /// Use this method when you need performance optimization but can tolerate + /// non-deterministic precision. If you require guaranteed precision, use + /// [`mul_add`](Self::mul_add) (guaranteed one rounding) or the separate + /// multiply and add operations (guaranteed two roundings). + /// + /// If you want even more optimization opportunities and aren't concerned about + /// error bounds, consider using the algebraic operations such as + /// [`algebraic_mul`](Self::algebraic_mul) and [`algebraic_add`](Self::algebraic_add). + /// + /// # Examples + /// + /// ``` + /// #![feature(float_mul_add_relaxed)] + /// + /// let m = 10.0_f32; + /// let x = 4.0_f32; + /// let b = 60.0_f32; + /// + /// // The result may be computed as either: + /// // - (m * x) + b with fused rounding, or + /// // - (m * x) + b with separate roundings + /// let result = m.mul_add_relaxed(x, b); + /// + /// // For simple values, both approaches give the same result + /// assert_eq!(result, 100.0); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "float_mul_add_relaxed", issue = "151770")] + #[rustc_const_unstable(feature = "float_mul_add_relaxed", issue = "151770")] + #[inline] + pub const fn mul_add_relaxed(self, a: f32, b: f32) -> f32 { + intrinsics::fmuladdf32(self, a, b) + } } /// Experimental implementations of floating point functions in `core`. diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 566a6a7ec947f..3f1143dcec495 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -1610,6 +1610,56 @@ impl f64 { pub const fn algebraic_rem(self, rhs: f64) -> f64 { intrinsics::frem_algebraic(self, rhs) } + + /// Fused multiply-add with relaxed precision semantics. Computes `(self * a) + b`, + /// non-deterministically executing either a fused multiply-add (one rounding) or + /// two separate operations with intermediate rounding. + /// + /// The operation may be fused if the code generator determines that the target + /// instruction set has support for a fused operation and that it is more efficient + /// than separate multiply and add instructions. Whether fusion occurs is unspecified + /// and may depend on optimization level and context. + /// + /// # Precision + /// + /// Unlike [`mul_add`](Self::mul_add), this operation does not guarantee which + /// rounding behavior will occur. It may perform either: + /// - A fused multiply-add with one rounding (more accurate) + /// - Separate multiply and add operations with two roundings (less accurate) + /// + /// Use this method when you need performance optimization but can tolerate + /// non-deterministic precision. If you require guaranteed precision, use + /// [`mul_add`](Self::mul_add) (guaranteed one rounding) or the separate + /// multiply and add operations (guaranteed two roundings). + /// + /// If you want even more optimization opportunities and aren't concerned about + /// error bounds, consider using the algebraic operations such as + /// [`algebraic_mul`](Self::algebraic_mul) and [`algebraic_add`](Self::algebraic_add). + /// + /// # Examples + /// + /// ``` + /// #![feature(float_mul_add_relaxed)] + /// + /// let m = 10.0_f64; + /// let x = 4.0_f64; + /// let b = 60.0_f64; + /// + /// // The result may be computed as either: + /// // - (m * x) + b with fused rounding, or + /// // - (m * x) + b with separate roundings + /// let result = m.mul_add_relaxed(x, b); + /// + /// // For simple values, both approaches give the same result + /// assert_eq!(result, 100.0); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "float_mul_add_relaxed", issue = "151770")] + #[rustc_const_unstable(feature = "float_mul_add_relaxed", issue = "151770")] + #[inline] + pub const fn mul_add_relaxed(self, a: f64, b: f64) -> f64 { + intrinsics::fmuladdf64(self, a, b) + } } #[unstable(feature = "core_float_math", issue = "137578")] diff --git a/library/std/src/num/f32.rs b/library/std/src/num/f32.rs index 77e6824784605..55fd9a910bb51 100644 --- a/library/std/src/num/f32.rs +++ b/library/std/src/num/f32.rs @@ -222,6 +222,57 @@ impl f32 { core::f32::math::mul_add(self, a, b) } + /// Fused multiply-add with relaxed precision semantics. Computes `(self * a) + b`, + /// non-deterministically executing either a fused multiply-add (one rounding) or + /// two separate operations with intermediate rounding. + /// + /// The operation may be fused if the code generator determines that the target + /// instruction set has support for a fused operation and that it is more efficient + /// than separate multiply and add instructions. Whether fusion occurs is unspecified + /// and may depend on optimization level and context. + /// + /// # Precision + /// + /// Unlike [`mul_add`](Self::mul_add), this operation does not guarantee which + /// rounding behavior will occur. It may perform either: + /// - A fused multiply-add with one rounding (more accurate) + /// - Separate multiply and add operations with two roundings (less accurate) + /// + /// Use this method when you need performance optimization but can tolerate + /// non-deterministic precision. If you require guaranteed precision, use + /// [`mul_add`](Self::mul_add) (guaranteed one rounding) or the separate + /// multiply and add operations (guaranteed two roundings). + /// + /// If you want even more optimization opportunities and aren't concerned about + /// error bounds, consider using the algebraic operations such as + /// [`f32::algebraic_mul`] and [`f32::algebraic_add`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(float_mul_add_relaxed)] + /// + /// let m = 10.0_f32; + /// let x = 4.0_f32; + /// let b = 60.0_f32; + /// + /// // The result may be computed as either: + /// // - (m * x) + b with fused rounding, or + /// // - (m * x) + b with separate roundings + /// let result = m.mul_add_relaxed(x, b); + /// + /// // For simple values, both approaches give the same result + /// assert_eq!(result, 100.0); + /// ``` + #[rustc_allow_incoherent_impl] + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "float_mul_add_relaxed", issue = "151770")] + #[inline] + #[rustc_const_unstable(feature = "float_mul_add_relaxed", issue = "151770")] + pub const fn mul_add_relaxed(self, a: f32, b: f32) -> f32 { + core::f32::mul_add_relaxed(self, a, b) + } + /// Calculates Euclidean division, the matching method for `rem_euclid`. /// /// This computes the integer `n` such that diff --git a/library/std/src/num/f64.rs b/library/std/src/num/f64.rs index e0b9948a924db..4f680013eb610 100644 --- a/library/std/src/num/f64.rs +++ b/library/std/src/num/f64.rs @@ -222,6 +222,57 @@ impl f64 { core::f64::math::mul_add(self, a, b) } + /// Fused multiply-add with relaxed precision semantics. Computes `(self * a) + b`, + /// non-deterministically executing either a fused multiply-add (one rounding) or + /// two separate operations with intermediate rounding. + /// + /// The operation may be fused if the code generator determines that the target + /// instruction set has support for a fused operation and that it is more efficient + /// than separate multiply and add instructions. Whether fusion occurs is unspecified + /// and may depend on optimization level and context. + /// + /// # Precision + /// + /// Unlike [`mul_add`](Self::mul_add), this operation does not guarantee which + /// rounding behavior will occur. It may perform either: + /// - A fused multiply-add with one rounding (more accurate) + /// - Separate multiply and add operations with two roundings (less accurate) + /// + /// Use this method when you need performance optimization but can tolerate + /// non-deterministic precision. If you require guaranteed precision, use + /// [`mul_add`](Self::mul_add) (guaranteed one rounding) or the separate + /// multiply and add operations (guaranteed two roundings). + /// + /// If you want even more optimization opportunities and aren't concerned about + /// error bounds, consider using the algebraic operations such as + /// [`f64::algebraic_mul`] and [`f64::algebraic_add`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(float_mul_add_relaxed)] + /// + /// let m = 10.0_f64; + /// let x = 4.0_f64; + /// let b = 60.0_f64; + /// + /// // The result may be computed as either: + /// // - (m * x) + b with fused rounding, or + /// // - (m * x) + b with separate roundings + /// let result = m.mul_add_relaxed(x, b); + /// + /// // For simple values, both approaches give the same result + /// assert_eq!(result, 100.0); + /// ``` + #[rustc_allow_incoherent_impl] + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "float_mul_add_relaxed", issue = "151770")] + #[inline] + #[rustc_const_unstable(feature = "float_mul_add_relaxed", issue = "151770")] + pub const fn mul_add_relaxed(self, a: f64, b: f64) -> f64 { + core::f64::mul_add_relaxed(self, a, b) + } + /// Calculates Euclidean division, the matching method for `rem_euclid`. /// /// This computes the integer `n` such that diff --git a/tests/ui/intrinsics/float-mul-add-relaxed.rs b/tests/ui/intrinsics/float-mul-add-relaxed.rs new file mode 100644 index 0000000000000..8271be4c112b8 --- /dev/null +++ b/tests/ui/intrinsics/float-mul-add-relaxed.rs @@ -0,0 +1,49 @@ +//@ run-pass +//@ compile-flags: -O + +#![feature(float_mul_add_relaxed)] + +fn main() { + test_f32(); + test_f64(); +} + +fn test_f32() { + let a = 2.0_f32; + let b = 3.0_f32; + let c = 4.0_f32; + + let result = a.mul_add_relaxed(b, c); + assert_eq!(result, 10.0); + + // Test with values where precision matters less + let x = 1.0_f32; + let y = 1.0_f32; + let z = 1.0_f32; + assert_eq!(x.mul_add_relaxed(y, z), 2.0); + + // Test edge cases + assert!(f32::NAN.mul_add_relaxed(1.0, 1.0).is_nan()); + assert_eq!(f32::INFINITY.mul_add_relaxed(2.0, 1.0), f32::INFINITY); + assert_eq!(0.0_f32.mul_add_relaxed(f32::INFINITY, 1.0), 1.0); +} + +fn test_f64() { + let a = 2.0_f64; + let b = 3.0_f64; + let c = 4.0_f64; + + let result = a.mul_add_relaxed(b, c); + assert_eq!(result, 10.0); + + // Test with values where precision matters less + let x = 1.0_f64; + let y = 1.0_f64; + let z = 1.0_f64; + assert_eq!(x.mul_add_relaxed(y, z), 2.0); + + // Test edge cases + assert!(f64::NAN.mul_add_relaxed(1.0, 1.0).is_nan()); + assert_eq!(f64::INFINITY.mul_add_relaxed(2.0, 1.0), f64::INFINITY); + assert_eq!(0.0_f64.mul_add_relaxed(f64::INFINITY, 1.0), 1.0); +} From 31d81ae781c30259212f9f8ad67528b5021b155b Mon Sep 17 00:00:00 2001 From: Owen Kirchenstien Date: Wed, 28 Jan 2026 15:16:12 -0500 Subject: [PATCH 2/9] Fix trailing whitespace in test file --- tests/ui/intrinsics/float-mul-add-relaxed.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/ui/intrinsics/float-mul-add-relaxed.rs b/tests/ui/intrinsics/float-mul-add-relaxed.rs index 8271be4c112b8..61ab035b04551 100644 --- a/tests/ui/intrinsics/float-mul-add-relaxed.rs +++ b/tests/ui/intrinsics/float-mul-add-relaxed.rs @@ -12,16 +12,16 @@ fn test_f32() { let a = 2.0_f32; let b = 3.0_f32; let c = 4.0_f32; - + let result = a.mul_add_relaxed(b, c); assert_eq!(result, 10.0); - + // Test with values where precision matters less let x = 1.0_f32; let y = 1.0_f32; let z = 1.0_f32; assert_eq!(x.mul_add_relaxed(y, z), 2.0); - + // Test edge cases assert!(f32::NAN.mul_add_relaxed(1.0, 1.0).is_nan()); assert_eq!(f32::INFINITY.mul_add_relaxed(2.0, 1.0), f32::INFINITY); @@ -32,16 +32,16 @@ fn test_f64() { let a = 2.0_f64; let b = 3.0_f64; let c = 4.0_f64; - + let result = a.mul_add_relaxed(b, c); assert_eq!(result, 10.0); - + // Test with values where precision matters less let x = 1.0_f64; let y = 1.0_f64; let z = 1.0_f64; assert_eq!(x.mul_add_relaxed(y, z), 2.0); - + // Test edge cases assert!(f64::NAN.mul_add_relaxed(1.0, 1.0).is_nan()); assert_eq!(f64::INFINITY.mul_add_relaxed(2.0, 1.0), f64::INFINITY); From f6b5e5bdf695142e769906fdd28175c1c76fca2e Mon Sep 17 00:00:00 2001 From: Owen Kirchenstien Date: Wed, 28 Jan 2026 16:06:13 -0500 Subject: [PATCH 3/9] Fix std implementation and trailing whitespace - Use core::intrinsics::fmuladd* directly in std impl - Remove trailing whitespace from test file --- library/std/src/num/f32.rs | 2 +- library/std/src/num/f64.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/num/f32.rs b/library/std/src/num/f32.rs index 55fd9a910bb51..a6e34448a4add 100644 --- a/library/std/src/num/f32.rs +++ b/library/std/src/num/f32.rs @@ -270,7 +270,7 @@ impl f32 { #[inline] #[rustc_const_unstable(feature = "float_mul_add_relaxed", issue = "151770")] pub const fn mul_add_relaxed(self, a: f32, b: f32) -> f32 { - core::f32::mul_add_relaxed(self, a, b) + core::intrinsics::fmuladdf32(self, a, b) } /// Calculates Euclidean division, the matching method for `rem_euclid`. diff --git a/library/std/src/num/f64.rs b/library/std/src/num/f64.rs index 4f680013eb610..40e352f80f18c 100644 --- a/library/std/src/num/f64.rs +++ b/library/std/src/num/f64.rs @@ -270,7 +270,7 @@ impl f64 { #[inline] #[rustc_const_unstable(feature = "float_mul_add_relaxed", issue = "151770")] pub const fn mul_add_relaxed(self, a: f64, b: f64) -> f64 { - core::f64::mul_add_relaxed(self, a, b) + core::intrinsics::fmuladdf64(self, a, b) } /// Calculates Euclidean division, the matching method for `rem_euclid`. From e70ad51c273cf1c1c2c5657cf30900765a8f8341 Mon Sep 17 00:00:00 2001 From: Owen Kirchenstien Date: Wed, 28 Jan 2026 17:02:04 -0500 Subject: [PATCH 4/9] removed definitions --- library/std/src/num/f32.rs | 51 -------------------------------------- library/std/src/num/f64.rs | 51 -------------------------------------- 2 files changed, 102 deletions(-) diff --git a/library/std/src/num/f32.rs b/library/std/src/num/f32.rs index a6e34448a4add..77e6824784605 100644 --- a/library/std/src/num/f32.rs +++ b/library/std/src/num/f32.rs @@ -222,57 +222,6 @@ impl f32 { core::f32::math::mul_add(self, a, b) } - /// Fused multiply-add with relaxed precision semantics. Computes `(self * a) + b`, - /// non-deterministically executing either a fused multiply-add (one rounding) or - /// two separate operations with intermediate rounding. - /// - /// The operation may be fused if the code generator determines that the target - /// instruction set has support for a fused operation and that it is more efficient - /// than separate multiply and add instructions. Whether fusion occurs is unspecified - /// and may depend on optimization level and context. - /// - /// # Precision - /// - /// Unlike [`mul_add`](Self::mul_add), this operation does not guarantee which - /// rounding behavior will occur. It may perform either: - /// - A fused multiply-add with one rounding (more accurate) - /// - Separate multiply and add operations with two roundings (less accurate) - /// - /// Use this method when you need performance optimization but can tolerate - /// non-deterministic precision. If you require guaranteed precision, use - /// [`mul_add`](Self::mul_add) (guaranteed one rounding) or the separate - /// multiply and add operations (guaranteed two roundings). - /// - /// If you want even more optimization opportunities and aren't concerned about - /// error bounds, consider using the algebraic operations such as - /// [`f32::algebraic_mul`] and [`f32::algebraic_add`]. - /// - /// # Examples - /// - /// ``` - /// #![feature(float_mul_add_relaxed)] - /// - /// let m = 10.0_f32; - /// let x = 4.0_f32; - /// let b = 60.0_f32; - /// - /// // The result may be computed as either: - /// // - (m * x) + b with fused rounding, or - /// // - (m * x) + b with separate roundings - /// let result = m.mul_add_relaxed(x, b); - /// - /// // For simple values, both approaches give the same result - /// assert_eq!(result, 100.0); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[unstable(feature = "float_mul_add_relaxed", issue = "151770")] - #[inline] - #[rustc_const_unstable(feature = "float_mul_add_relaxed", issue = "151770")] - pub const fn mul_add_relaxed(self, a: f32, b: f32) -> f32 { - core::intrinsics::fmuladdf32(self, a, b) - } - /// Calculates Euclidean division, the matching method for `rem_euclid`. /// /// This computes the integer `n` such that diff --git a/library/std/src/num/f64.rs b/library/std/src/num/f64.rs index 40e352f80f18c..e0b9948a924db 100644 --- a/library/std/src/num/f64.rs +++ b/library/std/src/num/f64.rs @@ -222,57 +222,6 @@ impl f64 { core::f64::math::mul_add(self, a, b) } - /// Fused multiply-add with relaxed precision semantics. Computes `(self * a) + b`, - /// non-deterministically executing either a fused multiply-add (one rounding) or - /// two separate operations with intermediate rounding. - /// - /// The operation may be fused if the code generator determines that the target - /// instruction set has support for a fused operation and that it is more efficient - /// than separate multiply and add instructions. Whether fusion occurs is unspecified - /// and may depend on optimization level and context. - /// - /// # Precision - /// - /// Unlike [`mul_add`](Self::mul_add), this operation does not guarantee which - /// rounding behavior will occur. It may perform either: - /// - A fused multiply-add with one rounding (more accurate) - /// - Separate multiply and add operations with two roundings (less accurate) - /// - /// Use this method when you need performance optimization but can tolerate - /// non-deterministic precision. If you require guaranteed precision, use - /// [`mul_add`](Self::mul_add) (guaranteed one rounding) or the separate - /// multiply and add operations (guaranteed two roundings). - /// - /// If you want even more optimization opportunities and aren't concerned about - /// error bounds, consider using the algebraic operations such as - /// [`f64::algebraic_mul`] and [`f64::algebraic_add`]. - /// - /// # Examples - /// - /// ``` - /// #![feature(float_mul_add_relaxed)] - /// - /// let m = 10.0_f64; - /// let x = 4.0_f64; - /// let b = 60.0_f64; - /// - /// // The result may be computed as either: - /// // - (m * x) + b with fused rounding, or - /// // - (m * x) + b with separate roundings - /// let result = m.mul_add_relaxed(x, b); - /// - /// // For simple values, both approaches give the same result - /// assert_eq!(result, 100.0); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[unstable(feature = "float_mul_add_relaxed", issue = "151770")] - #[inline] - #[rustc_const_unstable(feature = "float_mul_add_relaxed", issue = "151770")] - pub const fn mul_add_relaxed(self, a: f64, b: f64) -> f64 { - core::intrinsics::fmuladdf64(self, a, b) - } - /// Calculates Euclidean division, the matching method for `rem_euclid`. /// /// This computes the integer `n` such that From a91fe1aacaf1d05fa0705d5f23208ca74af1996f Mon Sep 17 00:00:00 2001 From: Owen Kirchenstien Date: Wed, 28 Jan 2026 20:43:19 -0500 Subject: [PATCH 5/9] Mark f16/f128 doc examples as ignore These doc tests require platform-specific f16/f128 support and should not be run during standard doc builds. --- library/core/src/num/f128.rs | 2 +- library/core/src/num/f16.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index 4d816bc10acfb..0cc7c8886d0e3 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -1477,7 +1477,7 @@ impl f128 { /// /// # Examples /// - /// ``` + /// ```ignore (f128 support is platform-specific) /// #![feature(f128)] /// #![feature(float_mul_add_relaxed)] /// # #[cfg(reliable_f128)] { diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index 2569799a04d05..7aef5671ac802 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -1462,7 +1462,7 @@ impl f16 { /// /// # Examples /// - /// ``` + /// ```ignore (f16 support is platform-specific) /// #![feature(f16)] /// #![feature(float_mul_add_relaxed)] /// # #[cfg(reliable_f16)] { From 586dd564976ba6ad2f3789109c24c0b3da020009 Mon Sep 17 00:00:00 2001 From: Owen Kirchenstien Date: Thu, 29 Jan 2026 11:31:50 -0500 Subject: [PATCH 6/9] Fix test and add cfg gates per reviewer feedback - Fix test: 0.0 * INFINITY = NaN, not 1.0 (IEEE 754) - Add #[cfg(target_has_reliable_f16_math)] gate for f16 - Add #[cfg(target_has_reliable_f128_math)] gate for f128 - Remove 'ignore' from doc tests as they now have proper cfg gates --- library/core/src/num/f128.rs | 3 ++- library/core/src/num/f16.rs | 3 ++- tests/ui/intrinsics/float-mul-add-relaxed.rs | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index 0cc7c8886d0e3..6baae8bbe638f 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -1477,7 +1477,7 @@ impl f128 { /// /// # Examples /// - /// ```ignore (f128 support is platform-specific) + /// ``` /// #![feature(f128)] /// #![feature(float_mul_add_relaxed)] /// # #[cfg(reliable_f128)] { @@ -1495,6 +1495,7 @@ impl f128 { /// assert_eq!(result, 100.0); /// # } /// ``` + #[cfg(target_has_reliable_f128_math)] #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "float_mul_add_relaxed", issue = "151770")] #[rustc_const_unstable(feature = "float_mul_add_relaxed", issue = "151770")] diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index 7aef5671ac802..aaeebcd8ec3e2 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -1462,7 +1462,7 @@ impl f16 { /// /// # Examples /// - /// ```ignore (f16 support is platform-specific) + /// ``` /// #![feature(f16)] /// #![feature(float_mul_add_relaxed)] /// # #[cfg(reliable_f16)] { @@ -1480,6 +1480,7 @@ impl f16 { /// assert_eq!(result, 100.0); /// # } /// ``` + #[cfg(target_has_reliable_f16_math)] #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "float_mul_add_relaxed", issue = "151770")] #[rustc_const_unstable(feature = "float_mul_add_relaxed", issue = "151770")] diff --git a/tests/ui/intrinsics/float-mul-add-relaxed.rs b/tests/ui/intrinsics/float-mul-add-relaxed.rs index 61ab035b04551..ff88156bddc6c 100644 --- a/tests/ui/intrinsics/float-mul-add-relaxed.rs +++ b/tests/ui/intrinsics/float-mul-add-relaxed.rs @@ -25,7 +25,7 @@ fn test_f32() { // Test edge cases assert!(f32::NAN.mul_add_relaxed(1.0, 1.0).is_nan()); assert_eq!(f32::INFINITY.mul_add_relaxed(2.0, 1.0), f32::INFINITY); - assert_eq!(0.0_f32.mul_add_relaxed(f32::INFINITY, 1.0), 1.0); + assert!(0.0_f32.mul_add_relaxed(f32::INFINITY, 1.0).is_nan()); } fn test_f64() { @@ -45,5 +45,5 @@ fn test_f64() { // Test edge cases assert!(f64::NAN.mul_add_relaxed(1.0, 1.0).is_nan()); assert_eq!(f64::INFINITY.mul_add_relaxed(2.0, 1.0), f64::INFINITY); - assert_eq!(0.0_f64.mul_add_relaxed(f64::INFINITY, 1.0), 1.0); + assert!(0.0_f64.mul_add_relaxed(f64::INFINITY, 1.0).is_nan()); } From de939cf9e7f91d2e2b015d32129bf15c7b735d46 Mon Sep 17 00:00:00 2001 From: Owen Kirchenstien Date: Thu, 29 Jan 2026 13:29:19 -0500 Subject: [PATCH 7/9] Fix broken intra-doc links in mul_add_relaxed documentation --- library/core/src/num/f32.rs | 4 ++-- library/core/src/num/f64.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 553e8815ade41..cd843364a352f 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -1624,14 +1624,14 @@ impl f32 { /// /// # Precision /// - /// Unlike [`mul_add`](Self::mul_add), this operation does not guarantee which + /// Unlike [`mul_add`](f32::mul_add), this operation does not guarantee which /// rounding behavior will occur. It may perform either: /// - A fused multiply-add with one rounding (more accurate) /// - Separate multiply and add operations with two roundings (less accurate) /// /// Use this method when you need performance optimization but can tolerate /// non-deterministic precision. If you require guaranteed precision, use - /// [`mul_add`](Self::mul_add) (guaranteed one rounding) or the separate + /// [`mul_add`](f32::mul_add) (guaranteed one rounding) or the separate /// multiply and add operations (guaranteed two roundings). /// /// If you want even more optimization opportunities and aren't concerned about diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 3f1143dcec495..414b510950842 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -1622,14 +1622,14 @@ impl f64 { /// /// # Precision /// - /// Unlike [`mul_add`](Self::mul_add), this operation does not guarantee which + /// Unlike [`mul_add`](f64::mul_add), this operation does not guarantee which /// rounding behavior will occur. It may perform either: /// - A fused multiply-add with one rounding (more accurate) /// - Separate multiply and add operations with two roundings (less accurate) /// /// Use this method when you need performance optimization but can tolerate /// non-deterministic precision. If you require guaranteed precision, use - /// [`mul_add`](Self::mul_add) (guaranteed one rounding) or the separate + /// [`mul_add`](f64::mul_add) (guaranteed one rounding) or the separate /// multiply and add operations (guaranteed two roundings). /// /// If you want even more optimization opportunities and aren't concerned about From 8dc75b94eeea014022a12fecffcb1e49ae94fd4e Mon Sep 17 00:00:00 2001 From: Owen Kirchenstien Date: Sat, 31 Jan 2026 18:16:41 -0500 Subject: [PATCH 8/9] fixed broken intra-doc links by using Self::mul_add instead of f32::mul_add/f64::mul_add + added examples --- library/core/src/num/f32.rs | 9 +++++++-- library/core/src/num/f64.rs | 9 +++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index cd843364a352f..55c34e7812dfd 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -1624,14 +1624,14 @@ impl f32 { /// /// # Precision /// - /// Unlike [`mul_add`](f32::mul_add), this operation does not guarantee which + /// Unlike [`mul_add`](Self::mul_add), this operation does not guarantee which /// rounding behavior will occur. It may perform either: /// - A fused multiply-add with one rounding (more accurate) /// - Separate multiply and add operations with two roundings (less accurate) /// /// Use this method when you need performance optimization but can tolerate /// non-deterministic precision. If you require guaranteed precision, use - /// [`mul_add`](f32::mul_add) (guaranteed one rounding) or the separate + /// [`mul_add`](Self::mul_add) (guaranteed one rounding) or the separate /// multiply and add operations (guaranteed two roundings). /// /// If you want even more optimization opportunities and aren't concerned about @@ -1654,6 +1654,11 @@ impl f32 { /// /// // For simple values, both approaches give the same result /// assert_eq!(result, 100.0); + /// + /// // Here's an example where the two approaches produce different results. + /// // The exact result depends on whether fusion occurs: + /// let r = 0.1_f32.mul_add_relaxed(0.1_f32, -0.01_f32); + /// assert!(r == 9.313226e-10 || r == 5.2154064e-10); /// ``` #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "float_mul_add_relaxed", issue = "151770")] diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 414b510950842..30c4889070478 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -1622,14 +1622,14 @@ impl f64 { /// /// # Precision /// - /// Unlike [`mul_add`](f64::mul_add), this operation does not guarantee which + /// Unlike [`mul_add`](Self::mul_add), this operation does not guarantee which /// rounding behavior will occur. It may perform either: /// - A fused multiply-add with one rounding (more accurate) /// - Separate multiply and add operations with two roundings (less accurate) /// /// Use this method when you need performance optimization but can tolerate /// non-deterministic precision. If you require guaranteed precision, use - /// [`mul_add`](f64::mul_add) (guaranteed one rounding) or the separate + /// [`mul_add`](Self::mul_add) (guaranteed one rounding) or the separate /// multiply and add operations (guaranteed two roundings). /// /// If you want even more optimization opportunities and aren't concerned about @@ -1652,6 +1652,11 @@ impl f64 { /// /// // For simple values, both approaches give the same result /// assert_eq!(result, 100.0); + /// + /// // Here's an example where the two approaches produce different results. + /// // The exact result depends on whether fusion occurs: + /// let r = 0.1_f64.mul_add_relaxed(0.1_f64, -0.01_f64); + /// assert!(r == 9.313225746154785e-10 || r == 2.7755575615628914e-17); /// ``` #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "float_mul_add_relaxed", issue = "151770")] From 0ca5122a47a67d1f32c7f5ddf06291a99a02b8d9 Mon Sep 17 00:00:00 2001 From: Owen Kirchenstien Date: Tue, 3 Feb 2026 18:17:27 -0500 Subject: [PATCH 9/9] fixed rustdoc broken links --- library/core/src/num/f32.rs | 6 ++++-- library/core/src/num/f64.rs | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 55c34e7812dfd..32148c3c46d76 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -1624,14 +1624,16 @@ impl f32 { /// /// # Precision /// - /// Unlike [`mul_add`](Self::mul_add), this operation does not guarantee which + /// Unlike [`mul_add`](https://doc.rust-lang.org/std/primitive.f32.html#method.mul_add), + /// this operation does not guarantee which /// rounding behavior will occur. It may perform either: /// - A fused multiply-add with one rounding (more accurate) /// - Separate multiply and add operations with two roundings (less accurate) /// /// Use this method when you need performance optimization but can tolerate /// non-deterministic precision. If you require guaranteed precision, use - /// [`mul_add`](Self::mul_add) (guaranteed one rounding) or the separate + /// [`mul_add`](https://doc.rust-lang.org/std/primitive.f32.html#method.mul_add) + /// (guaranteed one rounding) or the separate /// multiply and add operations (guaranteed two roundings). /// /// If you want even more optimization opportunities and aren't concerned about diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 30c4889070478..5b05b8e18a42f 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -1622,14 +1622,16 @@ impl f64 { /// /// # Precision /// - /// Unlike [`mul_add`](Self::mul_add), this operation does not guarantee which + /// Unlike [`mul_add`](https://doc.rust-lang.org/std/primitive.f64.html#method.mul_add), + /// this operation does not guarantee which /// rounding behavior will occur. It may perform either: /// - A fused multiply-add with one rounding (more accurate) /// - Separate multiply and add operations with two roundings (less accurate) /// /// Use this method when you need performance optimization but can tolerate /// non-deterministic precision. If you require guaranteed precision, use - /// [`mul_add`](Self::mul_add) (guaranteed one rounding) or the separate + /// [`mul_add`](https://doc.rust-lang.org/std/primitive.f64.html#method.mul_add) + /// (guaranteed one rounding) or the separate /// multiply and add operations (guaranteed two roundings). /// /// If you want even more optimization opportunities and aren't concerned about