twofloat/
arithmetic.rs

1#![allow(clippy::extra_unused_lifetimes)]
2
3use core::ops::{
4    Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign,
5};
6
7use crate::TwoFloat;
8
9// MinGW FMA seems to be inaccurate, use libm even if std is enabled.
10#[cfg(all(feature = "std", not(all(windows, target_env = "gnu"))))]
11#[inline(always)]
12fn fma(x: f64, y: f64, z: f64) -> f64 {
13    f64::mul_add(x, y, z)
14}
15
16#[cfg(not(all(feature = "std", not(all(windows, target_env = "gnu")))))]
17#[inline(always)]
18fn fma(x: f64, y: f64, z: f64) -> f64 {
19    libm::fma(x, y, z)
20}
21
22pub(crate) fn fast_two_sum(a: f64, b: f64) -> TwoFloat {
23    // Joldes et al. (2017) Algorithm 1
24    let s = a + b;
25    let z = s - a;
26    TwoFloat { hi: s, lo: b - z }
27}
28
29impl TwoFloat {
30    /// Creates a new `TwoFloat` by adding two `f64` values using Algorithm 2
31    /// from Joldes et al. (2017).
32    pub fn new_add(a: f64, b: f64) -> Self {
33        let s = a + b;
34        let aa = s - b;
35        let bb = s - aa;
36        let da = a - aa;
37        let db = b - bb;
38        Self { hi: s, lo: da + db }
39    }
40
41    /// Creates a new `TwoFloat` by subtracting two `f64` values using
42    /// Algorithm 2 from Joldes et al. (2017) modified for negative right-hand
43    /// side.
44    pub fn new_sub(a: f64, b: f64) -> Self {
45        let s = a - b;
46        let aa = s + b;
47        let bb = s - aa;
48        let da = a - aa;
49        let db = b + bb;
50        Self { hi: s, lo: da - db }
51    }
52
53    /// Creates a new `TwoFloat` by multiplying two `f64` values using
54    /// Algorithm 3 from Joldes et al. (2017).
55    pub fn new_mul(a: f64, b: f64) -> Self {
56        let p = a * b;
57        Self {
58            hi: p,
59            lo: fma(a, b, -p),
60        }
61    }
62
63    /// Creates a new `TwoFloat` by dividing two `f64` values using Algorithm
64    /// 15 from Joldes et al. (2017) modified for the left-hand-side having a
65    /// zero value in the low word.
66    pub fn new_div(a: f64, b: f64) -> Self {
67        let th = a / b;
68        let (ph, pl) = Self::new_mul(th, b).into();
69        let dh = a - ph;
70        let d = dh - pl;
71        let tl = d / b;
72        fast_two_sum(th, tl)
73    }
74}
75
76unary_ops! {
77    fn Neg::neg(self: &TwoFloat) -> TwoFloat {
78        Self::Output {
79            hi: -self.hi,
80            lo: -self.lo,
81        }
82    }
83}
84
85binary_ops! {
86    /// Implements addition of `TwoFloat` and `f64` using Joldes et al.
87    /// (2017) Algorithm 4.
88    fn Add::add<'a, 'b>(self: &'a TwoFloat, rhs: &'b f64) -> TwoFloat {
89        let (sh, sl) = TwoFloat::new_add(self.hi, *rhs).into();
90        let v = self.lo + sl;
91        fast_two_sum(sh, v)
92    }
93
94    /// Implements addition of `TwoFloat` and `f64` using Joldes et al.
95    /// (2017) Algorithm 4.
96    fn Add::add<'a, 'b>(self: &'a f64, rhs: &'b TwoFloat) -> TwoFloat {
97        let (sh, sl) = TwoFloat::new_add(rhs.hi, *self).into();
98        let v = rhs.lo + sl;
99        fast_two_sum(sh, v)
100    }
101
102    /// Implements addition of two `TwoFloat` values using Joldes et al.
103    /// (2017) Algorithm 6.
104    fn Add::add<'a, 'b>(self: &'a TwoFloat, rhs: &'b TwoFloat) -> TwoFloat {
105        let (sh, sl) = TwoFloat::new_add(self.hi, rhs.hi).into();
106        let (th, tl) = TwoFloat::new_add(self.lo, rhs.lo).into();
107        let c = sl + th;
108        let (vh, vl) = fast_two_sum(sh, c).into();
109        let w = tl + vl;
110        fast_two_sum(vh, w)
111    }
112
113    /// Implements subtraction of `TwoFloat` and `f64` using Joldes et al.
114    /// (2017) Algorithm 4 modified for negative right-hand side.
115    fn Sub::sub<'a, 'b>(self: &'a TwoFloat, rhs: &'b f64) -> TwoFloat {
116        let (sh, sl) = TwoFloat::new_sub(self.hi, *rhs).into();
117        let v = self.lo + sl;
118        fast_two_sum(sh, v)
119    }
120
121    /// Implements subtraction of `f64` and `TwoFloat` using Joldes et al.
122    /// (2017) Algorithm 4 modified for negative left-hand side.
123    fn Sub::sub<'a, 'b>(self: &'a f64, rhs: &'b TwoFloat) -> TwoFloat {
124        let (sh, sl) = TwoFloat::new_sub(*self, rhs.hi).into();
125        let v = sl - rhs.lo;
126        fast_two_sum(sh, v)
127    }
128
129    /// Implements subtraction of two `TwoFloat` values using Joldes et al.
130    /// (2017) Algorithm 6 modified for a negative right-hand side.
131    fn Sub::sub<'a, 'b>(self: &'a TwoFloat, rhs: &'b TwoFloat) -> TwoFloat {
132        let (sh, sl) = TwoFloat::new_sub(self.hi, rhs.hi).into();
133        let (th, tl) = TwoFloat::new_sub(self.lo, rhs.lo).into();
134        let c = sl + th;
135        let (vh, vl) = fast_two_sum(sh, c).into();
136        let w = tl + vl;
137        fast_two_sum(vh, w)
138    }
139
140    /// Implements multiplication of `TwoFloat` and `f64` using Joldes et al.
141    /// (2017) Algorithm 9.
142    fn Mul::mul<'a, 'b>(self: &'a TwoFloat, rhs: &'b f64) -> TwoFloat {
143        let (ch, cl1) = TwoFloat::new_mul(self.hi, *rhs).into();
144        let cl3 = fma(self.lo, *rhs, cl1);
145        fast_two_sum(ch, cl3)
146    }
147
148    /// Implements multiplication of `TwoFloat` and `f64` using Joldes et al.
149    /// (2017) Algorithm 9.
150    fn Mul::mul<'a, 'b>(self: &'a f64, rhs: &'b TwoFloat) -> TwoFloat {
151        let (ch, cl1) = TwoFloat::new_mul(rhs.hi, *self).into();
152        let cl3 = fma(rhs.lo, *self, cl1);
153        fast_two_sum(ch, cl3)
154    }
155
156    /// Implements multiplication of two `TwoFloat` values using Joldes et al.
157    /// (2017) Algorithm 12.
158    fn Mul::mul<'a, 'b>(self: &'a TwoFloat, rhs: &'b TwoFloat) -> TwoFloat {
159        let (ch, cl1) = TwoFloat::new_mul(self.hi, rhs.hi).into();
160        let tl0 = self.lo * rhs.lo;
161        let tl1 = fma(self.hi, rhs.lo, tl0);
162        let cl2 = fma(self.lo, rhs.hi, tl1);
163        let cl3 = cl1 + cl2;
164        fast_two_sum(ch, cl3)
165    }
166
167    /// Implements division of `TwoFloat` and `f64` using Joldes et al. (2017)
168    /// Algorithm 15
169    fn Div::div<'a, 'b>(self: &'a TwoFloat, rhs: &'b f64) -> TwoFloat {
170        let th = self.hi / rhs;
171        let (ph, pl) = TwoFloat::new_mul(th, *rhs).into();
172        let dh = self.hi - ph;
173        let dt = dh - pl;
174        let d = dt + self.lo;
175        let tl = d / rhs;
176        fast_two_sum(th, tl)
177    }
178
179    /// Implements division of `f64` and `TwoFloat` using Joldes et al. (2017)
180    /// Algorithm 18 modified for the left-hand side having a zero value in
181    /// the low word.
182    fn Div::div<'a, 'b>(self: &'a f64, rhs: &'b TwoFloat) -> TwoFloat {
183        let th = rhs.hi.recip();
184        let rh = 1.0 - rhs.hi * th;
185        let rl = -(rhs.lo * th);
186        let (eh, el) = fast_two_sum(rh, rl).into();
187        let e = TwoFloat { hi: eh, lo: el };
188        let d = e * th;
189        let m = d + th;
190        let (ch, cl1) = TwoFloat::new_mul(m.hi, *self).into();
191        let cl3 = fma(m.lo, *self, cl1);
192        fast_two_sum(ch, cl3)
193    }
194
195    /// Implements division of two `TwoFloat` values using Joldes et al.
196    /// (2017) Algorithm 18.
197    fn Div::div<'a, 'b>(self: &'a TwoFloat, rhs: &'b TwoFloat) -> TwoFloat {
198        let th = rhs.hi.recip();
199        let rh = 1.0 - rhs.hi * th;
200        let rl = -(rhs.lo * th);
201        let (eh, el) = fast_two_sum(rh, rl).into();
202        let e = TwoFloat { hi: eh, lo: el };
203        let d = e * th;
204        let m = d + th;
205        self * m
206    }
207
208    fn Rem::rem<'a, 'b>(self: &'a TwoFloat, rhs: &'b f64) -> TwoFloat {
209        let quotient = (self / rhs).trunc();
210        self - quotient * rhs
211    }
212
213    fn Rem::rem<'a, 'b>(self: &'a f64, rhs: &'b TwoFloat) -> TwoFloat {
214        let quotient = (self / rhs).trunc();
215        self - quotient * rhs
216    }
217
218    fn Rem::rem<'a, 'b>(self: &'a TwoFloat, rhs: &'b TwoFloat) -> TwoFloat {
219        let quotient = (self / rhs).trunc();
220        self - quotient * rhs
221    }
222}
223
224// Self-assignment operators
225
226assign_ops! {
227    /// Implements addition of `TwoFloat` and `f64` using Joldes et al.
228    /// (2017) Algorithm 4.
229    fn AddAssign::add_assign<'a>(self: &mut TwoFloat, rhs: &'a f64) {
230        let (sh, sl) = TwoFloat::new_add(self.hi, *rhs).into();
231        let v = self.lo + sl;
232        *self = fast_two_sum(sh, v);
233    }
234
235    /// Implements addition of two `TwoFloat` values using Joldes et al.
236    /// (2017) Algorithm 6.
237    fn AddAssign::add_assign<'a>(self: &mut TwoFloat, rhs: &'a TwoFloat) {
238        let (sh, sl) = TwoFloat::new_add(self.hi, rhs.hi).into();
239        let (th, tl) = TwoFloat::new_add(self.lo, rhs.lo).into();
240        let c = sl + th;
241        let (vh, vl) = fast_two_sum(sh, c).into();
242        let w = tl + vl;
243        *self = fast_two_sum(vh, w)
244    }
245
246    /// Implements subtraction of `TwoFloat` and `f64` using Joldes et al.
247    /// (2017) Algorithm 4 modified for negative right-hand side.
248    fn SubAssign::sub_assign<'a>(self: &mut TwoFloat, rhs: &'a f64) {
249        let (sh, sl) = TwoFloat::new_sub(self.hi, *rhs).into();
250        let v = self.lo + sl;
251        *self = fast_two_sum(sh, v);
252    }
253
254    /// Implements subtraction of two `TwoFloat` values using Joldes et al.
255    /// (2017) Algorithm 6 modified for a negative right-hand side.
256    fn SubAssign::sub_assign<'a>(self: &mut TwoFloat, rhs: &'a TwoFloat) {
257        let (sh, sl) = TwoFloat::new_sub(self.hi, rhs.hi).into();
258        let (th, tl) = TwoFloat::new_sub(self.lo, rhs.lo).into();
259        let c = sl + th;
260        let (vh, vl) = fast_two_sum(sh, c).into();
261        let w = tl + vl;
262        *self = fast_two_sum(vh, w)
263    }
264
265    /// Implements multiplication of `TwoFloat` and `f64` using Joldes et al.
266    /// (2017) Algorithm 9.
267    fn MulAssign::mul_assign<'a>(self: &mut TwoFloat, rhs: &'a f64) {
268        let (ch, cl1) = TwoFloat::new_mul(self.hi, *rhs).into();
269        let cl3 = fma(self.lo, *rhs, cl1);
270        *self = fast_two_sum(ch, cl3);
271    }
272
273    /// Implements multiplication of two `TwoFloat` values using Joldes et al.
274    /// (2017) Algorithm 12.
275    fn MulAssign::mul_assign<'a>(self: &mut TwoFloat, rhs: &'a TwoFloat) {
276        let (ch, cl1) = TwoFloat::new_mul(self.hi, rhs.hi).into();
277        let tl0 = self.lo * rhs.lo;
278        let tl1 = fma(self.hi, rhs.lo, tl0);
279        let cl2 = fma(self.lo, rhs.hi, tl1);
280        let cl3 = cl1 + cl2;
281        *self = fast_two_sum(ch, cl3)
282    }
283
284    /// Implements division of `TwoFloat` and `f64` using Joldes et al. (2017)
285    /// Algorithm 15
286    fn DivAssign::div_assign<'a>(self: &mut TwoFloat, rhs: &'a f64) {
287        let th = self.hi / rhs;
288        let (ph, pl) = TwoFloat::new_mul(th, *rhs).into();
289        let dh = self.hi - ph;
290        let dt = dh - pl;
291        let d = dt + self.lo;
292        let tl = d / rhs;
293        *self = fast_two_sum(th, tl)
294    }
295
296    /// Implements division of two `TwoFloat` values using Joldes et al.
297    /// (2017) Algorithm 18.
298    fn DivAssign::div_assign<'a>(self: &mut TwoFloat, rhs: &'a TwoFloat) {
299        let th = rhs.hi.recip();
300        let rh = 1.0 - rhs.hi * th;
301        let rl = -(rhs.lo * th);
302        let (eh, el) = fast_two_sum(rh, rl).into();
303        let e = TwoFloat { hi: eh, lo: el };
304        let d = e * th;
305        let m = d + th;
306        *self *= m;
307    }
308
309    fn RemAssign::rem_assign<'b>(self: &mut TwoFloat, rhs: &'b f64) {
310        let quotient = (*self / rhs).trunc();
311        *self -= quotient * rhs;
312    }
313
314    fn RemAssign::rem_assign<'a>(self: &mut TwoFloat, rhs: &'a TwoFloat) {
315        let quotient = (*self / rhs).trunc();
316        *self -= quotient * rhs;
317    }
318}
319
320impl TwoFloat {
321    /// Calculates Euclidean division, the matching method for `rem_euclid`.
322    ///
323    /// # Examples
324    ///
325    /// ```
326    /// # use twofloat::TwoFloat;
327    /// let a = TwoFloat::from(9.0);
328    /// let b = TwoFloat::from(5.0);
329    ///
330    /// assert_eq!(a.div_euclid(b), TwoFloat::from(1.0));
331    /// assert_eq!((-a).div_euclid(b), TwoFloat::from(-2.0));
332    /// assert_eq!(a.div_euclid(-b), TwoFloat::from(-1.0));
333    /// assert_eq!((-a).div_euclid(-b), TwoFloat::from(2.0));
334    /// ```
335    pub fn div_euclid(self, rhs: Self) -> Self {
336        let quotient = (self / rhs).trunc();
337        if (self - quotient * rhs) < 0.0 {
338            if rhs > 0.0 {
339                quotient - 1.0
340            } else {
341                quotient + 1.0
342            }
343        } else {
344            quotient
345        }
346    }
347
348    /// Calculates the least nonnegative remainder of `self (mod rhs)`.
349    ///
350    /// The return value `r` usually satisfies `0.0 <= r < rhs.abs()`,
351    /// although the errors in numerical computation may result in violations
352    /// of this constraint.
353    ///
354    /// # Examples
355    ///
356    /// ```
357    /// # use twofloat::TwoFloat;
358    /// let a = TwoFloat::from(9.0);
359    /// let b = TwoFloat::from(5.0);
360    ///
361    /// assert_eq!(a.rem_euclid(b), TwoFloat::from(4.0));
362    /// assert_eq!((-a).rem_euclid(b), TwoFloat::from(1.0));
363    /// assert_eq!(a.rem_euclid(-b), TwoFloat::from(4.0));
364    /// assert_eq!((-a).rem_euclid(-b), TwoFloat::from(1.0));
365    /// ```
366    pub fn rem_euclid(self, rhs: Self) -> Self {
367        let remainder = self % rhs;
368        if remainder < 0.0 {
369            remainder + rhs.abs()
370        } else {
371            remainder
372        }
373    }
374}
375
376#[cfg(test)]
377mod tests {
378    use super::fast_two_sum;
379    use crate::test_util::{get_valid_pair, repeated_test};
380
381    #[test]
382    fn fast_two_sum_test() {
383        repeated_test(|| {
384            let (a, b) = get_valid_pair(|x, y| (x + y).is_finite());
385            let result = if a.abs() >= b.abs() {
386                fast_two_sum(a, b)
387            } else {
388                fast_two_sum(b, a)
389            };
390
391            assert_eq_ulp!(
392                result.hi(),
393                a + b,
394                1,
395                "Incorrect result of fast_two_sum({}, {})",
396                a,
397                b
398            );
399            assert!(
400                result.is_valid(),
401                "Invalid result of fast_two_sum({}, {})",
402                a,
403                b
404            );
405        });
406    }
407}