Added `MulAdd` and `MulAddAssign` traits

This commit is contained in:
Vincent Esche 2018-04-09 10:21:51 +02:00
parent bb67a3d03a
commit 830363024b
3 changed files with 159 additions and 0 deletions

View File

@ -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};

View File

@ -2,3 +2,4 @@ pub mod saturating;
pub mod checked;
pub mod wrapping;
pub mod inv;
pub mod mul_add;

157
src/ops/mul_add.rs Normal file
View File

@ -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<A = Self, B = Self> {
/// 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<A = Self, B = Self> {
/// Performs the fused multiply-add operation.
fn mul_add_assign(&mut self, a: A, b: B);
}
impl MulAdd<f32, f32> 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<f64, f64> 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<f32, f32> 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<f64, f64> 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);
}
}