From 830363024b28c67f4799a08d0b4997667f382936 Mon Sep 17 00:00:00 2001 From: Vincent Esche Date: Mon, 9 Apr 2018 10:21:51 +0200 Subject: [PATCH 1/3] Added `MulAdd` and `MulAddAssign` traits --- src/lib.rs | 1 + src/ops/mod.rs | 1 + src/ops/mul_add.rs | 157 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 159 insertions(+) create mode 100644 src/ops/mul_add.rs diff --git a/src/lib.rs b/src/lib.rs index 3e1c80c0..7f394488 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -36,6 +36,7 @@ pub use identities::{Zero, One, zero, one}; pub use ops::inv::Inv; pub use ops::checked::{CheckedAdd, CheckedSub, CheckedMul, CheckedDiv, CheckedShl, CheckedShr}; pub use ops::wrapping::{WrappingAdd, WrappingMul, WrappingSub}; +pub use ops::mul_add::{MulAdd, MulAddAssign}; pub use ops::saturating::Saturating; pub use sign::{Signed, Unsigned, abs, abs_sub, signum}; pub use cast::{AsPrimitive, FromPrimitive, ToPrimitive, NumCast, cast}; diff --git a/src/ops/mod.rs b/src/ops/mod.rs index 7b9a7e35..93b51959 100644 --- a/src/ops/mod.rs +++ b/src/ops/mod.rs @@ -2,3 +2,4 @@ pub mod saturating; pub mod checked; pub mod wrapping; pub mod inv; +pub mod mul_add; diff --git a/src/ops/mul_add.rs b/src/ops/mul_add.rs new file mode 100644 index 00000000..4b3ca2ad --- /dev/null +++ b/src/ops/mul_add.rs @@ -0,0 +1,157 @@ +/// The fused multiply-add operation. +/// Computes (self * a) + b with only one rounding error. +/// This produces a more accurate result with better performance +/// than a separate multiplication operation followed by an add. +/// +/// Note that `A` and `B` are `Self` by default, but this is not mandatory. +/// +/// # Example +/// +/// ``` +/// use std::f32; +/// +/// let m = 10.0_f32; +/// let x = 4.0_f32; +/// let b = 60.0_f32; +/// +/// // 100.0 +/// let abs_difference = (m.mul_add(x, b) - (m*x + b)).abs(); +/// +/// assert!(abs_difference <= f32::EPSILON); +/// ``` +pub trait MulAdd { + /// The resulting type after applying the fused multiply-add. + type Output; + + /// Performs the fused multiply-add operation. + fn mul_add(self, a: A, b: B) -> Self::Output; +} + +/// The fused multiply-add assignment operation. +pub trait MulAddAssign { + /// Performs the fused multiply-add operation. + fn mul_add_assign(&mut self, a: A, b: B); +} + +impl MulAdd for f32 { + type Output = Self; + + #[inline] + fn mul_add(self, a: Self, b: Self) -> Self::Output { + if cfg!(feature = "std") { + f32::mul_add(self, a, b) + } else { + (self * a) + b + } + } +} + +impl MulAdd for f64 { + type Output = Self; + + #[inline] + fn mul_add(self, a: Self, b: Self) -> Self::Output { + if cfg!(feature = "std") { + f64::mul_add(self, a, b) + } else { + (self * a) + b + } + } +} + +macro_rules! mul_add_impl { + ($trait_name:ident for $($t:ty)*) => {$( + impl $trait_name for $t { + type Output = Self; + + #[inline] + fn mul_add(self, a: Self, b: Self) -> Self::Output { + (self * a) + b + } + } + )*} +} + +mul_add_impl!(MulAdd for isize usize i8 u8 i16 u16 i32 u32 i64 u64); + +impl MulAddAssign for f32 { + #[inline] + fn mul_add_assign(&mut self, a: Self, b: Self) { + if cfg!(feature = "std") { + *self = f32::mul_add(*self, a, b) + } else { + *self = (*self * a) + b + } + } +} + +impl MulAddAssign for f64 { + #[inline] + fn mul_add_assign(&mut self, a: Self, b: Self) { + if cfg!(feature = "std") { + *self = f64::mul_add(*self, a, b) + } else { + *self = (*self * a) + b + } + } +} + +macro_rules! mul_add_assign_impl { + ($trait_name:ident for $($t:ty)*) => {$( + impl $trait_name for $t { + #[inline] + fn mul_add_assign(&mut self, a: Self, b: Self) { + *self = (*self * a) + b + } + } + )*} +} + +mul_add_assign_impl!(MulAddAssign for isize usize i8 u8 i16 u16 i32 u32 i64 u64); + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn mul_add_integer() { + macro_rules! test_mul_add { + ($($t:ident)+) => { + $( + { + let m: $t = 2; + let x: $t = 3; + let b: $t = 4; + + assert_eq!(m.mul_add(x, b), (m*x + b)); + } + )+ + }; + } + + test_mul_add!(usize u8 u16 u32 u64 isize i8 i16 i32 i64); + } + + #[test] + fn mul_add_float() { + macro_rules! test_mul_add { + ($($t:ident)+) => { + $( + { + use core::$t; + + let m: $t = 12.0; + let x: $t = 3.4; + let b: $t = 5.6; + + let abs_difference = (m.mul_add(x, b) - (m*x + b)).abs(); + + assert!(abs_difference <= $t::EPSILON); + } + )+ + }; + } + + test_mul_add!(f32 f64); + } +} From 28be8854815b5ec8fdf1513df3910b54a4763e49 Mon Sep 17 00:00:00 2001 From: Vincent Esche Date: Wed, 18 Apr 2018 10:31:37 +0200 Subject: [PATCH 2/3] Moved impl of `MulAdd`/`MulAddAssign` for `f32`/`f64` behind feature guard --- src/ops/mul_add.rs | 28 ++++++++-------------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/src/ops/mul_add.rs b/src/ops/mul_add.rs index 4b3ca2ad..9a2a2cf5 100644 --- a/src/ops/mul_add.rs +++ b/src/ops/mul_add.rs @@ -33,29 +33,23 @@ pub trait MulAddAssign { fn mul_add_assign(&mut self, a: A, b: B); } +#[cfg(feature = "std")] impl MulAdd for f32 { type Output = Self; #[inline] fn mul_add(self, a: Self, b: Self) -> Self::Output { - if cfg!(feature = "std") { - f32::mul_add(self, a, b) - } else { - (self * a) + b - } + f32::mul_add(self, a, b) } } +#[cfg(feature = "std")] impl MulAdd for f64 { type Output = Self; #[inline] fn mul_add(self, a: Self, b: Self) -> Self::Output { - if cfg!(feature = "std") { - f64::mul_add(self, a, b) - } else { - (self * a) + b - } + f64::mul_add(self, a, b) } } @@ -74,25 +68,19 @@ macro_rules! mul_add_impl { mul_add_impl!(MulAdd for isize usize i8 u8 i16 u16 i32 u32 i64 u64); +#[cfg(feature = "std")] impl MulAddAssign for f32 { #[inline] fn mul_add_assign(&mut self, a: Self, b: Self) { - if cfg!(feature = "std") { - *self = f32::mul_add(*self, a, b) - } else { - *self = (*self * a) + b - } + *self = f32::mul_add(*self, a, b) } } +#[cfg(feature = "std")] impl MulAddAssign for f64 { #[inline] fn mul_add_assign(&mut self, a: Self, b: Self) { - if cfg!(feature = "std") { - *self = f64::mul_add(*self, a, b) - } else { - *self = (*self * a) + b - } + *self = f64::mul_add(*self, a, b) } } From 0d358034d9ab1cf3f758aed55f98483bb9c37ba7 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Fri, 4 May 2018 12:09:02 -0700 Subject: [PATCH 3/3] Test MulAdd explicitly, guarded by std for floats --- src/ops/mul_add.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ops/mul_add.rs b/src/ops/mul_add.rs index 9a2a2cf5..284ee9f5 100644 --- a/src/ops/mul_add.rs +++ b/src/ops/mul_add.rs @@ -111,7 +111,7 @@ mod tests { let x: $t = 3; let b: $t = 4; - assert_eq!(m.mul_add(x, b), (m*x + b)); + assert_eq!(MulAdd::mul_add(m, x, b), (m*x + b)); } )+ }; @@ -121,6 +121,7 @@ mod tests { } #[test] + #[cfg(feature = "std")] fn mul_add_float() { macro_rules! test_mul_add { ($($t:ident)+) => { @@ -132,7 +133,7 @@ mod tests { let x: $t = 3.4; let b: $t = 5.6; - let abs_difference = (m.mul_add(x, b) - (m*x + b)).abs(); + let abs_difference = (MulAdd::mul_add(m, x, b) - (m*x + b)).abs(); assert!(abs_difference <= $t::EPSILON); }