pxfm/triple_double.rs
1/*
2 * // Copyright (c) Radzivon Bartoshyk 7/2025. All rights reserved.
3 * //
4 * // Redistribution and use in source and binary forms, with or without modification,
5 * // are permitted provided that the following conditions are met:
6 * //
7 * // 1. Redistributions of source code must retain the above copyright notice, this
8 * // list of conditions and the following disclaimer.
9 * //
10 * // 2. Redistributions in binary form must reproduce the above copyright notice,
11 * // this list of conditions and the following disclaimer in the documentation
12 * // and/or other materials provided with the distribution.
13 * //
14 * // 3. Neither the name of the copyright holder nor the names of its
15 * // contributors may be used to endorse or promote products derived from
16 * // this software without specific prior written permission.
17 * //
18 * // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 * // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 * // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 * // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 * // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 * // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29use crate::common::f_fmla;
30use crate::double_double::DoubleDouble;
31use std::ops::Neg;
32
33#[derive(Clone, Copy, Debug)]
34pub(crate) struct TripleDouble {
35 pub(crate) hi: f64,
36 pub(crate) mid: f64,
37 pub(crate) lo: f64,
38}
39
40// impl TripleDouble {
41// #[inline]
42// pub(crate) fn mul_dd_add_f64(p0: TripleDouble, p1: DoubleDouble, p2: f64) -> TripleDouble {
43// let q0 = TripleDouble::quick_mul_dd(p0, p1);
44// TripleDouble::add_f64(p2, q0)
45// }
46// }
47
48impl TripleDouble {
49 // #[inline]
50 // pub(crate) fn mul_dd_add(p0: TripleDouble, p1: DoubleDouble, p2: TripleDouble) -> TripleDouble {
51 // let q0 = TripleDouble::quick_mul_dd(p0, p1);
52 // TripleDouble::add(q0, p2)
53 // }
54
55 // #[inline]
56 // pub(crate) fn mul_dd_add_dd(
57 // p0: TripleDouble,
58 // p1: DoubleDouble,
59 // p2: DoubleDouble,
60 // ) -> TripleDouble {
61 // let q0 = TripleDouble::quick_mul_dd(p0, p1);
62 // TripleDouble::add_dd(p2, q0)
63 // }
64}
65
66impl TripleDouble {
67 #[inline]
68 pub(crate) fn f64_mul_dd_add(p0: f64, p1: DoubleDouble, p2: TripleDouble) -> TripleDouble {
69 let q0 = TripleDouble::from_quick_mult_dd_f64(p1, p0);
70 TripleDouble::add(q0, p2)
71 }
72
73 #[inline]
74 pub(crate) fn f64_mul_add(p0: f64, p1: TripleDouble, p2: TripleDouble) -> TripleDouble {
75 let q0 = TripleDouble::quick_mult_f64(p1, p0);
76 TripleDouble::add(q0, p2)
77 }
78}
79
80impl TripleDouble {
81 #[inline]
82 pub(crate) const fn from_bit_pair(p0: (u64, u64, u64)) -> TripleDouble {
83 TripleDouble {
84 hi: f64::from_bits(p0.2),
85 mid: f64::from_bits(p0.1),
86 lo: f64::from_bits(p0.0),
87 }
88 }
89}
90
91#[inline]
92fn add12(a: f64, b: f64) -> DoubleDouble {
93 DoubleDouble::from_full_exact_add(a, b)
94}
95
96#[inline]
97fn mul12(a: f64, b: f64) -> DoubleDouble {
98 DoubleDouble::from_exact_mult(a, b)
99}
100
101#[inline]
102pub(crate) fn add22(a: DoubleDouble, b: DoubleDouble) -> DoubleDouble {
103 let DoubleDouble { hi: v1, lo: v2 } = DoubleDouble::from_full_exact_add(a.hi, b.hi);
104 let v3 = a.lo + b.lo;
105 let v4 = v2 + v3;
106 DoubleDouble::from_full_exact_add(v1, v4)
107}
108
109impl TripleDouble {
110 // #[inline]
111 // pub(crate) fn from_quick_mult_dd(a: DoubleDouble, b: DoubleDouble) -> TripleDouble {
112 // /*
113 // (rh , t1) ← Mul12 (ah , bh )
114 // (t2, t3) ← Mul12 (ah , bl )
115 // (t4, t5) ← Mul12 (al , bh )
116 // t6 ← al ⊗ bl
117 // (t7, t8) ← Add22 (t2, t3, t4, t5)
118 // (t9, t10) ← Add12 (t1, t6)
119 // (rm , rl ) ← Add22 (t7, t8, t9, t10)
120 // */
121 // let DoubleDouble { hi: rh, lo: t1 } = mul12(a.hi, b.hi);
122 // let r0 = mul12(a.hi, b.lo);
123 // let r1 = mul12(a.lo, b.hi);
124 // let t6 = a.lo * b.lo;
125 // let q0 = add22(r0, r1);
126 // let q1 = add12(t1, t6);
127 // let DoubleDouble { hi: rm, lo: rl } = add22(q0, q1);
128 // TripleDouble {
129 // hi: rh,
130 // mid: rm,
131 // lo: rl,
132 // }
133 // }
134
135 #[inline]
136 pub(crate) fn from_quick_mult_dd_f64(a: DoubleDouble, b: f64) -> TripleDouble {
137 let DoubleDouble { hi: rh, lo: t1 } = mul12(a.hi, b);
138 let DoubleDouble { hi: t2, lo: t3 } = mul12(a.lo, b);
139 let DoubleDouble { hi: t5, lo: t4 } = add12(t1, t2);
140 let t6 = t3 + t4;
141 let DoubleDouble { hi: rm, lo: rl } = add12(t5, t6);
142 TripleDouble::new(rl, rm, rh)
143 }
144
145 #[inline]
146 pub(crate) fn quick_mult_f64(a: TripleDouble, b: f64) -> TripleDouble {
147 let DoubleDouble { hi: rh, lo: t2 } = mul12(a.hi, b);
148 let DoubleDouble { hi: t3, lo: t4 } = mul12(a.mid, b);
149 let DoubleDouble { hi: t9, lo: t7 } = add12(t2, t3);
150 let t8 = f_fmla(a.lo, b, t4);
151 let t10 = t7 + t8;
152 let DoubleDouble { hi: rm, lo: rl } = add12(t9, t10);
153 TripleDouble::new(rl, rm, rh)
154 }
155
156 #[inline]
157 pub(crate) fn quick_mult(b: TripleDouble, a: TripleDouble) -> TripleDouble {
158 /* Mul12((resh),&_t1,(ah),(bh));
159 Mul12(&_t2,&_t3,(ah),(bm));
160 Mul12(&_t4,&_t5,(am),(bh));
161 Mul12(&_t6,&_t7,(am),(bm));
162 _t8 = (ah) * (bl);
163 _t9 = (al) * (bh);
164 _t10 = (am) * (bl);
165 _t11 = (al) * (bm);
166 _t12 = _t8 + _t9;
167 _t13 = _t10 + _t11;
168 Add12Cond(_t14,_t15,_t1,_t6);
169 _t16 = _t7 + _t15;
170 _t17 = _t12 + _t13;
171 _t18 = _t16 + _t17;
172 Add12Cond(_t19,_t20,_t14,_t18);
173 Add22Cond(&_t21,&_t22,_t2,_t3,_t4,_t5);
174 Add22Cond((resm),(resl),_t21,_t22,_t19,_t20); */
175 let DoubleDouble { hi: rh, lo: t1 } = DoubleDouble::from_exact_mult(a.hi, b.hi);
176 let DoubleDouble { hi: t2, lo: t3 } = DoubleDouble::from_exact_mult(a.hi, b.mid);
177 let DoubleDouble { hi: t4, lo: t5 } = DoubleDouble::from_exact_mult(a.mid, b.hi);
178 let DoubleDouble { hi: t6, lo: t7 } = DoubleDouble::from_exact_mult(a.mid, b.mid);
179 let t12 = f_fmla(a.hi, b.lo, a.lo * b.hi);
180 let t13 = f_fmla(a.mid, b.lo, a.lo * b.mid);
181 let DoubleDouble { hi: t14, lo: t15 } = add12(t1, t6);
182 let t16 = t7 + t15;
183 let t17 = t12 + t13;
184 let t18 = t16 + t17;
185 let DoubleDouble { hi: t19, lo: t20 } = add12(t14, t18);
186 let DoubleDouble { hi: t21, lo: t22 } =
187 add22(DoubleDouble::new(t3, t2), DoubleDouble::new(t4, t5));
188 let DoubleDouble { hi: rm, lo: rl } =
189 add22(DoubleDouble::new(t22, t21), DoubleDouble::new(t20, t19));
190 TripleDouble::new(rl, rm, rh)
191 }
192
193 #[inline]
194 pub(crate) fn quick_mult_dd(b: TripleDouble, a: DoubleDouble) -> TripleDouble {
195 /*
196 (rh , t1) ← Mul12 (ah , bh )
197 (t2, t3) ← Mul12 (ah , bm )
198 (t4, t5) ← Mul12 (ah , bl )
199 (t6, t7) ← Mul12 (al , bh )
200 (t8, t9) ← Mul12 (al , bm )
201 t10 ← al ⊗ bl
202 (t11, t12) ← Add22 (t2, t3, t4, t5)
203 (t13, t14) ← Add22 (t6, t7, t8, t9)
204 (t15, t16) ← Add22 (t11, t12, t13, t14)
205 (t17, t18) ← Add12 (t1, t10)
206 (rm , rl ) ← Add22 (t17, t18, t15, t16)
207 */
208 let DoubleDouble { hi: rh, lo: t1 } = mul12(a.hi, b.hi);
209 let DoubleDouble { hi: t2, lo: t3 } = mul12(a.hi, b.mid);
210 let DoubleDouble { hi: t4, lo: t5 } = mul12(a.hi, b.lo);
211 let DoubleDouble { hi: t6, lo: t7 } = mul12(a.lo, b.hi);
212 let DoubleDouble { hi: t8, lo: t9 } = mul12(a.lo, b.mid);
213 let t10 = a.lo * b.lo;
214 let z0 = add22(DoubleDouble::new(t3, t2), DoubleDouble::new(t4, t5));
215 let z1 = add22(DoubleDouble::new(t6, t7), DoubleDouble::new(t8, t9));
216 let q0 = add22(z0, z1);
217 let q1 = add12(t1, t10);
218 let DoubleDouble { hi: rm, lo: rl } = add22(q1, q0);
219 TripleDouble {
220 hi: rh,
221 mid: rm,
222 lo: rl,
223 }
224 }
225
226 pub(crate) fn add(a: TripleDouble, b: TripleDouble) -> TripleDouble {
227 /*
228 (rh , t1) ← Add12 (ah , bh )
229 (t2, t3) ← Add12 (am , bm )
230 (t7, t4) ← Add12 (t1, t2)
231 t6 ← al ⊕ bl
232 t5 ← t3 ⊕ t4
233 t8 ← t5 ⊕ t6
234 (rm , rl ) ← Add12 (t7, t8)
235 */
236 let DoubleDouble { hi: rh, lo: t1 } = add12(a.hi, b.hi);
237 let DoubleDouble { hi: t2, lo: t3 } = add12(a.mid, b.mid);
238 let t6 = a.lo + b.lo;
239 let DoubleDouble { hi: t7, lo: t4 } = add12(t1, t2);
240 let t5 = t3 + t4;
241 let t8 = t5 + t6;
242 let DoubleDouble { hi: rm, lo: rl } = add12(t7, t8);
243 TripleDouble {
244 hi: rh,
245 mid: rm,
246 lo: rl,
247 }
248 }
249
250 // #[inline]
251 // pub(crate) fn add_dd(a: DoubleDouble, b: TripleDouble) -> TripleDouble {
252 // /*
253 // (rh , t1) ← Add12 (ah , bh )
254 // (t2, t3) ← Add12 (al , bm )
255 // (t4, t5) ← Add12 (t1, t2)
256 // t6 ← t3 ⊕ bl
257 // t7 ← t6 ⊕ t5
258 // (rm , rl ) ← Add12 (t4, t7)
259 // */
260 // let DoubleDouble { hi: rh, lo: t1 } = add12(a.hi, b.hi);
261 // let DoubleDouble { hi: t2, lo: t3 } = add12(a.hi, b.mid);
262 // let DoubleDouble { hi: t4, lo: t5 } = add12(t1, t2);
263 // let t6 = t3 + b.lo;
264 // let t7 = t6 + t5;
265 // let DoubleDouble { hi: rm, lo: rl } = add12(t4, t7);
266 // TripleDouble {
267 // hi: rh,
268 // mid: rm,
269 // lo: rl,
270 // }
271 // }
272
273 #[inline]
274 pub(crate) fn add_f64(a: f64, b: TripleDouble) -> TripleDouble {
275 let DoubleDouble { hi: rh, lo: t1 } = add12(a, b.hi);
276 let DoubleDouble { hi: t2, lo: t3 } = add12(t1, b.mid);
277 let t4 = t3 + b.lo;
278 let DoubleDouble { hi: rm, lo: rl } = add12(t2, t4);
279 TripleDouble::new(rl, rm, rh)
280 }
281
282 // #[inline]
283 // pub(crate) fn to_dd(self) -> DoubleDouble {
284 // let DoubleDouble { hi: t1, lo: t2 } = add12(self.hi, self.mid);
285 // let t3 = t2 + self.lo;
286 // DoubleDouble::new(t3, t1)
287 // }
288
289 #[inline]
290 pub(crate) const fn new(lo: f64, mid: f64, hi: f64) -> Self {
291 Self { hi, mid, lo }
292 }
293
294 #[inline]
295 pub(crate) fn to_f64(self) -> f64 {
296 let DoubleDouble { hi: t1, lo: t2 } = add12(self.hi, self.mid);
297 let t3 = t2 + self.lo;
298 t1 + t3
299 }
300
301 #[inline]
302 pub(crate) fn from_full_exact_add(a: f64, b: f64) -> Self {
303 let DoubleDouble { hi: rh, lo: t1 } = add12(a, b);
304 TripleDouble::new(0., t1, rh).renormalize()
305 }
306
307 #[inline]
308 pub(crate) fn renormalize(self) -> Self {
309 /*
310 double _t1h, _t1l, _t2l;
311
312 Add12(_t1h, _t1l, (am), (al));
313 Add12((*(resh)), _t2l, (ah), (_t1h));
314 Add12((*(resm)), (*(resl)), _t2l, _t1l);
315 */
316 let DoubleDouble { hi: t1h, lo: t1l } = DoubleDouble::from_exact_add(self.mid, self.lo);
317 let DoubleDouble { hi: rh, lo: t2l } = DoubleDouble::from_exact_add(self.hi, t1h);
318 let zml = DoubleDouble::from_exact_add(t2l, t1l);
319 TripleDouble::new(zml.lo, zml.hi, rh)
320 }
321
322 #[inline]
323 pub(crate) fn recip(self) -> Self {
324 /*
325 _rec_r1 = 1.0 / (dh);
326 Mul12(&_rec_t1,&_rec_t2,_rec_r1,(dh));
327 _rec_t3 = _rec_t1 - 1.0;
328 Add12Cond(_rec_t4,_rec_t5,_rec_t3,_rec_t2);
329 Mul12(&_rec_t6,&_rec_t7,_rec_r1,(dm));
330 Add12(_rec_t8,_rec_t9,-1.0,_rec_t6);
331 _rec_t10 = _rec_t9 + _rec_t7;
332 Add12(_rec_t11,_rec_t12,_rec_t8,_rec_t10);
333 _rec_r1 = -_rec_r1;
334 Add22Cond(&_rec_t13,&_rec_t14,_rec_t4,_rec_t5,_rec_t11,_rec_t12);
335 Mul122(&_rec_r2h,&_rec_r2l,_rec_r1,_rec_t13,_rec_t14);
336 Mul233(&_rec_t15,&_rec_t16,&_rec_t17,_rec_r2h,_rec_r2l,(dh),(dm),(dl));
337 Renormalize3(&_rec_t18,&_rec_t19,&_rec_t20,_rec_t15,_rec_t16,_rec_t17);
338 _rec_t18 = -1.0;
339 Mul233(&_rec_t21,&_rec_t22,&_rec_t23,_rec_r2h,_rec_r2l,_rec_t18,_rec_t19,_rec_t20);
340 _rec_t21 = -_rec_t21; _rec_t22 = -_rec_t22; _rec_t23 = -_rec_t23;
341 Renormalize3((resh),(resm),(resl),_rec_t21,_rec_t22,_rec_t23);
342 */
343 let mut r1 = 1. / self.hi;
344 let DoubleDouble {
345 hi: rec_t1,
346 lo: rec_t2,
347 } = DoubleDouble::from_exact_mult(r1, self.hi);
348 let rec_t3 = rec_t1 - 1.0;
349 let DoubleDouble {
350 hi: rec_t4,
351 lo: rec_t5,
352 } = add12(rec_t3, rec_t2);
353 let DoubleDouble {
354 hi: rec_t6,
355 lo: rec_t7,
356 } = mul12(r1, self.mid);
357 let DoubleDouble {
358 hi: rec_t8,
359 lo: rec_t9,
360 } = add12(-1.0, rec_t6);
361 let rec_t10 = rec_t9 + rec_t7;
362 r1 = -r1;
363 let DoubleDouble {
364 hi: rec_t11,
365 lo: rec_t12,
366 } = add12(rec_t8, rec_t10);
367 let z0 = add22(
368 DoubleDouble::new(rec_t5, rec_t4),
369 DoubleDouble::new(rec_t12, rec_t11),
370 );
371 let r2hl = DoubleDouble::quick_mult_f64(z0, r1);
372 let rec15_16_17 = TripleDouble::quick_mult_dd(self, r2hl);
373 let rec18_19_20 = rec15_16_17.renormalize();
374 let rec_t18 = -1.0;
375 let rec_21_22_23 = -TripleDouble::quick_mult_dd(
376 TripleDouble::new(rec18_19_20.lo, rec18_19_20.mid, rec_t18),
377 r2hl,
378 );
379 rec_21_22_23.renormalize()
380 }
381}
382
383impl Neg for TripleDouble {
384 type Output = Self;
385 #[inline]
386 fn neg(self) -> Self::Output {
387 TripleDouble::new(-self.lo, -self.mid, -self.hi)
388 }
389}