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#[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 let s = a + b;
25 let z = s - a;
26 TwoFloat { hi: s, lo: b - z }
27}
28
29impl TwoFloat {
30 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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
224assign_ops! {
227 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 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 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 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 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 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 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 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 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 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}