Skip to main content

const_num_traits/ops/
mul_add.rs

1c0nst::c0nst! {
2/// Fused multiply-add. Computes `(self * a) + b` with only one rounding
3/// error, yielding a more accurate result than an unfused multiply-add.
4///
5/// Using `mul_add` can be more performant than an unfused multiply-add if
6/// the target architecture has a dedicated `fma` CPU instruction.
7///
8/// Note that `A` and `B` are `Self` by default, but this is not mandatory.
9///
10/// # Example
11///
12/// ```
13/// use std::f32;
14///
15/// let m = 10.0_f32;
16/// let x = 4.0_f32;
17/// let b = 60.0_f32;
18///
19/// // 100.0
20/// let abs_difference = (m.mul_add(x, b) - (m*x + b)).abs();
21///
22/// assert!(abs_difference <= 100.0 * f32::EPSILON);
23/// ```
24pub c0nst trait MulAdd<A = Self, B = Self> {
25    /// The resulting type after applying the fused multiply-add.
26    type Output;
27
28    /// Performs the fused multiply-add operation `(self * a) + b`
29    fn mul_add(self, a: A, b: B) -> Self::Output;
30}
31}
32
33c0nst::c0nst! {
34/// The fused multiply-add assignment operation `*self = (*self * a) + b`
35pub c0nst trait MulAddAssign<A = Self, B = Self> {
36    /// Performs the fused multiply-add assignment operation `*self = (*self * a) + b`
37    fn mul_add_assign(&mut self, a: A, b: B);
38}
39}
40
41// Float MulAdd / MulAddAssign delegate to `Float::mul_add`, which is non-const.
42#[cfg(any(feature = "std", feature = "libm"))]
43impl MulAdd<f32, f32> for f32 {
44    type Output = Self;
45
46    #[inline]
47    fn mul_add(self, a: Self, b: Self) -> Self::Output {
48        <Self as crate::Float>::mul_add(self, a, b)
49    }
50}
51
52#[cfg(any(feature = "std", feature = "libm"))]
53impl MulAdd<f64, f64> for f64 {
54    type Output = Self;
55
56    #[inline]
57    fn mul_add(self, a: Self, b: Self) -> Self::Output {
58        <Self as crate::Float>::mul_add(self, a, b)
59    }
60}
61
62macro_rules! mul_add_impl {
63    ($trait_name:ident for $($t:ty)*) => {$(
64        c0nst::c0nst! {
65        c0nst impl $trait_name for $t {
66            type Output = Self;
67
68            #[inline]
69            fn mul_add(self, a: Self, b: Self) -> Self::Output {
70                (self * a) + b
71            }
72        }
73        }
74    )*}
75}
76
77mul_add_impl!(MulAdd for isize i8 i16 i32 i64 i128);
78mul_add_impl!(MulAdd for usize u8 u16 u32 u64 u128);
79
80#[cfg(any(feature = "std", feature = "libm"))]
81impl MulAddAssign<f32, f32> for f32 {
82    #[inline]
83    fn mul_add_assign(&mut self, a: Self, b: Self) {
84        *self = <Self as crate::Float>::mul_add(*self, a, b)
85    }
86}
87
88#[cfg(any(feature = "std", feature = "libm"))]
89impl MulAddAssign<f64, f64> for f64 {
90    #[inline]
91    fn mul_add_assign(&mut self, a: Self, b: Self) {
92        *self = <Self as crate::Float>::mul_add(*self, a, b)
93    }
94}
95
96macro_rules! mul_add_assign_impl {
97    ($trait_name:ident for $($t:ty)*) => {$(
98        c0nst::c0nst! {
99        c0nst impl $trait_name for $t {
100            #[inline]
101            fn mul_add_assign(&mut self, a: Self, b: Self) {
102                *self = (*self * a) + b
103            }
104        }
105        }
106    )*}
107}
108
109mul_add_assign_impl!(MulAddAssign for isize i8 i16 i32 i64 i128);
110mul_add_assign_impl!(MulAddAssign for usize u8 u16 u32 u64 u128);
111
112#[cfg(test)]
113mod tests {
114    use super::*;
115
116    #[test]
117    fn mul_add_integer() {
118        macro_rules! test_mul_add {
119            ($($t:ident)+) => {
120                $(
121                    {
122                        let m: $t = 2;
123                        let x: $t = 3;
124                        let b: $t = 4;
125
126                        assert_eq!(MulAdd::mul_add(m, x, b), (m*x + b));
127                    }
128                )+
129            };
130        }
131
132        test_mul_add!(usize u8 u16 u32 u64 isize i8 i16 i32 i64);
133    }
134
135    #[test]
136    #[cfg(feature = "std")]
137    fn mul_add_float() {
138        macro_rules! test_mul_add {
139            ($($t:ident)+) => {
140                $(
141                    {
142                        use core::$t;
143
144                        let m: $t = 12.0;
145                        let x: $t = 3.4;
146                        let b: $t = 5.6;
147
148                        let abs_difference = (MulAdd::mul_add(m, x, b) - (m*x + b)).abs();
149
150                        assert!(abs_difference <= 46.4 * $t::EPSILON);
151                    }
152                )+
153            };
154        }
155
156        test_mul_add!(f32 f64);
157    }
158}