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..6baae8bbe638f 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -1449,6 +1449,60 @@ 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); + /// # } + /// ``` + #[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")] + #[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..aaeebcd8ec3e2 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -1434,6 +1434,60 @@ 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); + /// # } + /// ``` + #[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")] + #[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..32148c3c46d76 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -1612,6 +1612,63 @@ 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`](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`](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 + /// 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); + /// + /// // 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")] + #[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..5b05b8e18a42f 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -1610,6 +1610,63 @@ 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`](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`](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 + /// 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); + /// + /// // 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")] + #[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/tests/ui/intrinsics/float-mul-add-relaxed.rs b/tests/ui/intrinsics/float-mul-add-relaxed.rs new file mode 100644 index 0000000000000..ff88156bddc6c --- /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!(0.0_f32.mul_add_relaxed(f32::INFINITY, 1.0).is_nan()); +} + +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!(0.0_f64.mul_add_relaxed(f64::INFINITY, 1.0).is_nan()); +}