Skip to main content

thermite_compensated/
lib.rs

1#![doc = include_str!("../README.md")]
2#![no_std]
3#![cfg_attr(docsrs, feature(doc_cfg))]
4#![allow(unused_braces)]
5
6use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign};
7
8use num_traits::{NumAssignOps, NumOps};
9use thermite::Swizzle;
10use thermite::element::SignedElement;
11use thermite::generic_array::GenericArray;
12use thermite::register::SwizzleIndices;
13use thermite::tribool::{self, Tribool};
14use thermite::vector::{NewConst, NewVector, SplatConst, SplatVector, VectorValue, const_splat};
15use thermite::{LargeInt, mask::GenericSelectable, prelude::*};
16
17use thermite::vector::ops::{AddSubExt, AddSubExtMasked, MulAddAssignExt, MulAddExt, Square, SquareMasked};
18
19// # `algebraic-scalar` support
20//
21// Error-free transformations only work if the compiler evaluates them as written. Under
22// `algebraic-scalar` the scalar backend's arithmetic is reassociable, so LLVM can fold
23// `(a - (s - v)) + (b - v)` to zero and the error terms silently vanish.
24//
25// Two things keep that from happening here:
26//
27// - `ScalarValue for Vector<R>` delegates `two_sum`/`two_diff`/`two_prod`/`square` to
28//   `FloatVectorWithBits`, which the scalar backend overrides strict. `f32`/`f64` keep the
29//   default bodies, since element-level `+` is plain strict Rust.
30// - Operators that join a cancelling subtraction in bare `+`/`-` lose the second word even
31//   when every EFT is strict. Those sites (`division_remainder`, the remainder join in the
32//   three division entry points, `Sub`'s `self.error - rhs.error`) take a `two_diff`/`two_sum`
33//   high word instead. The residual is dead code, so it is free.
34//
35// `tests/algebraic_scalar.rs` pins this.
36
37pub mod consts;
38pub mod math;
39
40#[cfg(feature = "special")]
41pub mod special;
42pub mod specialized;
43
44/// Scalar values that can be used in compensated arithmetic.
45///
46/// This also applies to [`Vector`]s whose elements implement this trait.
47///
48/// This can be implemented for anything so long as a suitable Veltkamp's
49/// splitting constant can be provided. It just doesn't make much sense
50/// on anything but scalar-like floating point types.
51///
52/// If the type has native FMA support, as indicated by `MulAddExt::HAS_NATIVE_FMA`,
53/// the splitting constant is never used, so it can be a dummy value in that case.
54pub trait ScalarValue:
55    Copy + NumOps + NumAssignOps + MulAddExt<Output = Self> + Neg<Output = Self> + consts::SplitFloatConsts<Self>
56{
57    /// for Veltkamp's splitting
58    const SPLITTER: Self;
59
60    /// `|a|` above this overflows `a * SPLITTER`, so [`rebalance_for_split`] must bring
61    /// it down first.
62    ///
63    /// Need not be a power of two, and is not one for `f64`: that value is the largest
64    /// double strictly below `2^996`, which errs in the safe direction.
65    ///
66    /// [`rebalance_for_split`]: ScalarValue::rebalance_for_split
67    const SPLIT_THRESH: Self;
68
69    /// Exact power of two to scale a large operand down by before splitting.
70    const SPLIT_DOWN: Self;
71
72    /// Exact reciprocal of [`ScalarValue::SPLIT_DOWN`], also a power of two.
73    const SPLIT_UP: Self;
74
75    /// The value zero. Named this way to avoid conflicts.
76    const SCALAR_ZERO: Self;
77
78    /// The value one. Named this way to avoid conflicts.
79    const SCALAR_ONE: Self;
80
81    /// Empirical maximum |x| for which the erf_inv Maclaurin series converges
82    /// within 64 terms to full precision. This is only used when the `special`
83    /// crate feature is enabled, for the `erf_inv` function.
84    const MAX_ERFINV_SERIES: Self;
85
86    /// `|x|` at which `erf`/`erfc` hand over from the erf series to the erfc
87    /// continued fraction, when the policy asks for precision.
88    ///
89    /// The series computes `erf` and gets `erfc` as `1 - erf`, so `erfc` inherits
90    /// `erf`'s ABSOLUTE error and loses `log2(erf/erfc)` bits of relative accuracy.
91    /// The continued fraction computes `erfc` directly, with no cancellation, but
92    /// needs far more iterations the smaller `|x|` gets: a measured 338 double-double
93    /// Lentz steps at 1.5 against 202 at 2.0. Lowering this constant buys accuracy
94    /// with time.
95    ///
96    /// **Per type, because the two widths disagree about where the trade pays.** For
97    /// f64 double-double the continued fraction holds a flat ~2-7e-31 relative from 1.5
98    /// upward, comfortably better than the series' 6.9e-31 at 1.5 and 7.2e-30 at 1.821,
99    /// so 1.5 wins. For f32 double-single the continued fraction has a much worse
100    /// floor (a measured ~1.4-2.4e-12, roughly 400-700 ulp), and the series beats it
101    /// at every point from 1.25 to 2.25, so f32 keeps the historical 2.
102    ///
103    /// Only consulted when `PrecisionPolicy` is `Best` or above. Below that both types
104    /// use 2 regardless. See `erf_internal_p`.
105    const ERF_CF_SPLIT: Self;
106
107    /// Returns the value truncated to its integer component.
108    ///
109    /// Named this way to avoid conflicts. Required for the `Rem` implementation.
110    fn scalar_trunc(self) -> Self;
111
112    /// Marker type for splatting a compile-time integer constant as `Compensated<Self>`.
113    ///
114    /// Each concrete impl can choose the precision strategy: `f32` uses a `f64` intermediate
115    /// to capture the rounding error in the error term; `f64` stores zero error (would need
116    /// `f128` for better); `Vector<R>` delegates to the inner element and splats via
117    /// `VectorValue`.
118    type CompensatedConstInt<const N: LargeInt>: SplatConst<Compensated<Self>>;
119
120    /// Marker type for splatting a compile-time rational constant `N/D` as `Compensated<Self>`.
121    ///
122    /// Same precision strategy as `CompensatedConstInt`.
123    type CompensatedConstRatio<const N: LargeInt, const D: LargeInt>: SplatConst<Compensated<Self>>;
124
125    #[inline(always)]
126    fn two_sum(a: Self, b: Self) -> (Self, Self) {
127        let s = a + b;
128        let v = s - a;
129        let e = (a - (s - v)) + (b - v);
130        (s, e)
131    }
132
133    #[inline(always)]
134    fn two_diff(a: Self, b: Self) -> (Self, Self) {
135        let s = a - b;
136        let v = s - a;
137        let e = (a - (s - v)) - (b + v);
138        (s, e)
139    }
140
141    /// Veltkamp's splitting: `a == hi + lo` exactly, with each part narrow enough that
142    /// products of the parts are themselves exact.
143    ///
144    /// `a * SPLITTER` overflows once `|a|` passes `MAX / SPLITTER` - 8.3056e34 for f32,
145    /// 1.3394e300 for f64 - so callers must bring `a` under that first. [`two_prod`] does
146    /// it with [`rebalance_for_split`].
147    ///
148    /// [`SPLIT_THRESH`](ScalarValue::SPLIT_THRESH) sits at or below that point rather
149    /// than exactly on it: for f32 it is `2^115`, comfortably under, because the obvious
150    /// `2^116` is 8.3077e34 and so lands just *above* the real limit. That near miss was
151    /// a live bug.
152    ///
153    /// [`two_prod`]: ScalarValue::two_prod
154    /// [`rebalance_for_split`]: ScalarValue::rebalance_for_split
155    #[inline(always)]
156    fn veltkamp_split(a: Self) -> (Self, Self) {
157        let c = a * Self::SPLITTER;
158        let hi = c - (c - a);
159        (hi, a - hi)
160    }
161
162    /// Move an exact power of two from whichever operand is large enough to overflow
163    /// [`veltkamp_split`] into the other one, leaving `a * b` unchanged.
164    ///
165    /// Scaling the split's *output* back up does not work: for `a` near `MAX` the split
166    /// rounds `hi` up past `MAX / scale`, so scaling back overflows anyway. Rebalancing
167    /// the inputs against each other never has to scale anything back.
168    ///
169    /// The receiving operand cannot overflow. If `|a| > THRESH` and `a * b` is finite,
170    /// then `|b| < MAX / |a| <= MAX / THRESH`, which is exactly the scale factor - so
171    /// `b * scale` stays in range. When both operands are that large the true product is
172    /// already infinite, and an infinite result is the correct answer.
173    ///
174    /// # Residual limit
175    ///
176    /// One case remains, and it is inherent to Dekker's `two_prod` rather than to the
177    /// guard: the split rounds `hi` up by up to a relative `2^-53`, so `a_hi * b_hi`
178    /// slightly exceeds `a * b`. If `a * b` is within that relative distance of `MAX`,
179    /// that product overflows and the error term comes back infinite. The value is still
180    /// correct. `two_prod(MAX, 0.5)` is fine; only `two_prod(MAX, 1.0)` is affected.
181    ///
182    /// # Implementing
183    ///
184    /// Required rather than defaulted, because a default that ignored
185    /// [`SPLIT_THRESH`](ScalarValue::SPLIT_THRESH) would silently make those constants
186    /// dead and hand the implementor a `two_prod` that returns NaN on large operands. It
187    /// cannot be given a working default either: a generic body would need a lane-wise
188    /// comparison and select, which this trait's bounds do not provide.
189    ///
190    /// A scalar implementation branches; a vector one selects per lane. See the `f64` and
191    /// `Vector<R>` implementations below.
192    ///
193    /// [`veltkamp_split`]: ScalarValue::veltkamp_split
194    fn rebalance_for_split(a: Self, b: Self) -> (Self, Self);
195
196    #[inline(always)]
197    fn two_prod(a: Self, b: Self) -> (Self, Self) {
198        // fast path if we have FMA available
199        if matches!(Self::HAS_NATIVE_FMA, tribool::True) {
200            let p = a * b;
201            let e = a.mul_sub(b, p);
202
203            return (p, e);
204        }
205
206        // Guard the split against overflow. `p` uses the ORIGINAL operands; the rebalance
207        // preserves the product exactly, so the split parts describe the same value.
208        let (sa, sb) = Self::rebalance_for_split(a, b);
209        let (a_hi, a_lo) = Self::veltkamp_split(sa);
210        let (b_hi, b_lo) = Self::veltkamp_split(sb);
211
212        // exact product
213        let p = a * b;
214
215        let err = ((a_hi * b_hi - p) + a_hi * b_lo + a_lo * b_hi) + a_lo * b_lo;
216
217        (p, err)
218    }
219
220    #[inline(always)]
221    fn square(a: Self) -> (Self, Self) {
222        // fast path if we have FMA available
223        if matches!(Self::HAS_NATIVE_FMA, tribool::True) {
224            let p = a * a;
225            let e = a.mul_sub(a, p);
226
227            return (p, e);
228        }
229
230        // No overflow guard here: `veltkamp_split` only overflows above `MAX / SPLITTER`,
231        // and squaring anything that large already overflows `p` itself, so an infinite
232        // result is the correct answer.
233        let (a_hi, a_lo) = Self::veltkamp_split(a);
234
235        // exact product
236        let p = a * a;
237
238        let d = a_hi * a_lo;
239        let err = ((a_hi * a_hi - p) + d + d) + a_lo * a_lo;
240
241        (p, err)
242    }
243
244    /// 2Quotient: `(q, r)` with `q = RN(a / b)` and `a == q * b + r` exactly.
245    ///
246    /// `r` is a remainder, not a second word of the quotient: `a / b == q + r / b`.
247    ///
248    /// Mostly used for `q`. `two_quot(a, b).0` is a correctly-rounded division that
249    /// survives `algebraic-scalar`, where a bare `/` may become `x * RN(1/c)` (up to
250    /// 1.204 ulp measured). The unused remainder is dead code. See
251    /// [`FloatVectorWithBits::two_quot`](thermite::vector::FloatVectorWithBits::two_quot).
252    #[inline(always)]
253    fn two_quot(a: Self, b: Self) -> (Self, Self) {
254        let q = a / b;
255
256        if matches!(Self::HAS_NATIVE_FMA, tribool::True) {
257            return (q, q.nmul_add(b, a));
258        }
259
260        let (p, e) = Self::two_prod(q, b);
261
262        (q, (a - p) - e)
263    }
264}
265
266impl ScalarValue for f32 {
267    const SPLITTER: Self = ((1u64 << 12) + 1) as f32; // 2^12 + 1
268    const SPLIT_THRESH: Self = 4.153_837_5e34; // 2^115
269    const SPLIT_DOWN: Self = 1.220_703_1e-4; // 2^-13
270    const SPLIT_UP: Self = 8192.0; // 2^13
271    const SCALAR_ZERO: Self = 0.0;
272    const SCALAR_ONE: Self = 1.0;
273    const MAX_ERFINV_SERIES: Self = 0.75;
274    // The continued fraction is worse than the series everywhere below ~2.25 at this
275    // width, so f32 keeps the historical split and gains nothing from the policy gate.
276    const ERF_CF_SPLIT: Self = 2.0;
277
278    #[inline(always)]
279    #[allow(
280        clippy::manual_range_contains,
281        reason = "RangeInclusive::contains is false for NaN, so the negated form would send NaN down the rebalance path; the explicit comparison leaves it on the fast path"
282    )]
283    fn rebalance_for_split(a: Self, b: Self) -> (Self, Self) {
284        const THRESH: f32 = <f32 as ScalarValue>::SPLIT_THRESH;
285        const DOWN: f32 = <f32 as ScalarValue>::SPLIT_DOWN;
286        const UP: f32 = <f32 as ScalarValue>::SPLIT_UP;
287
288        if a > THRESH || a < -THRESH {
289            (a * DOWN, b * UP)
290        } else if b > THRESH || b < -THRESH {
291            (a * UP, b * DOWN)
292        } else {
293            (a, b)
294        }
295    }
296
297    #[inline(always)]
298    fn scalar_trunc(self) -> Self {
299        FloatElement::trunc(self)
300    }
301
302    type CompensatedConstInt<const N: LargeInt> = F32CompensatedIntConst<N>;
303    type CompensatedConstRatio<const N: LargeInt, const D: LargeInt> = F32CompensatedRatioConst<N, D>;
304}
305
306impl ScalarValue for f64 {
307    const SPLITTER: Self = ((1u64 << 27) + 1) as f64; // 2^27 + 1
308    const SPLIT_THRESH: Self = 6.69692879491417e299; // largest f64 below 2^996
309    const SPLIT_DOWN: Self = 3.725_290_298_461_914e-9; // 2^-28
310    const SPLIT_UP: Self = 268435456.0; // 2^28
311    const SCALAR_ZERO: Self = 0.0;
312    const SCALAR_ONE: Self = 1.0;
313    const MAX_ERFINV_SERIES: Self = 0.545;
314    const ERF_CF_SPLIT: Self = 1.5;
315
316    #[inline(always)]
317    #[allow(
318        clippy::manual_range_contains,
319        reason = "RangeInclusive::contains is false for NaN, so the negated form would send NaN down the rebalance path; the explicit comparison leaves it on the fast path"
320    )]
321    fn rebalance_for_split(a: Self, b: Self) -> (Self, Self) {
322        const THRESH: f64 = <f64 as ScalarValue>::SPLIT_THRESH;
323        const DOWN: f64 = <f64 as ScalarValue>::SPLIT_DOWN;
324        const UP: f64 = <f64 as ScalarValue>::SPLIT_UP;
325
326        if a > THRESH || a < -THRESH {
327            (a * DOWN, b * UP)
328        } else if b > THRESH || b < -THRESH {
329            (a * UP, b * DOWN)
330        } else {
331            (a, b)
332        }
333    }
334
335    #[inline(always)]
336    fn scalar_trunc(self) -> Self {
337        FloatElement::trunc(self)
338    }
339
340    type CompensatedConstInt<const N: LargeInt> = F64CompensatedIntConst<N>;
341    type CompensatedConstRatio<const N: LargeInt, const D: LargeInt> = F64CompensatedRatioConst<N, D>;
342}
343
344/// `SplatConst` carrier for [`ScalarValue::SPLITTER`] at a generic element type.
345///
346/// The `const_splat!` macro cannot generate this one: its carrier takes a single path
347/// bound per generic parameter, while this needs `E: ScalarValue` on the *element*, not
348/// on the vector. Hand-rolling the carrier is what keeps the vector impl below off the
349/// deprecated `Vector::splat_const`. Same pattern as the table carriers in `consts.rs`.
350struct SplitterValue<E>(core::marker::PhantomData<E>);
351
352impl<E: ScalarValue> SplatConst<E> for SplitterValue<E> {
353    const VALUE: E = <E as ScalarValue>::SPLITTER;
354}
355
356// The guard rests on three properties that the decimal literals on the impls do not make
357// obvious, so they are pinned at compile time.
358//
359// Note the thresholds are NOT themselves powers of two, and need not be: the f64 value is
360// the largest double strictly below 2^996 (the same literal Bailey's QD uses), which is
361// conservative in the right direction. What matters is only that splitting anything at or
362// below the threshold cannot overflow. An earlier f32 threshold of 2^116 passed casual
363// testing but failed exactly this check - `2^116 * 4097` exceeds `f32::MAX`.
364const _: () = {
365    // 1. The scale factors are exact reciprocals, so rebalancing preserves the product.
366    assert!(<f32 as ScalarValue>::SPLIT_DOWN * <f32 as ScalarValue>::SPLIT_UP == 1.0);
367    assert!(<f64 as ScalarValue>::SPLIT_DOWN * <f64 as ScalarValue>::SPLIT_UP == 1.0);
368
369    // 2. Both are powers of two, so scaling is lossless. A power of two has an all-zero
370    //    significand field.
371    assert!(<f32 as ScalarValue>::SPLIT_DOWN.to_bits() & ((1 << 23) - 1) == 0);
372    assert!(<f64 as ScalarValue>::SPLIT_DOWN.to_bits() & ((1 << 52) - 1) == 0);
373    assert!(<f32 as ScalarValue>::SPLIT_UP.to_bits() & ((1 << 23) - 1) == 0);
374    assert!(<f64 as ScalarValue>::SPLIT_UP.to_bits() & ((1 << 52) - 1) == 0);
375
376    // 3. Splitting is safe on both sides of the branch: at the threshold unscaled, and at
377    //    the largest finite value once scaled down.
378    assert!(<f32 as ScalarValue>::SPLIT_THRESH * <f32 as ScalarValue>::SPLITTER < f32::MAX);
379    assert!(<f64 as ScalarValue>::SPLIT_THRESH * <f64 as ScalarValue>::SPLITTER < f64::MAX);
380    assert!(f32::MAX * <f32 as ScalarValue>::SPLIT_DOWN * <f32 as ScalarValue>::SPLITTER < f32::MAX);
381    assert!(f64::MAX * <f64 as ScalarValue>::SPLIT_DOWN * <f64 as ScalarValue>::SPLITTER < f64::MAX);
382};
383
384/// `SplatConst` carrier for [`ScalarValue::SPLIT_THRESH`]. See [`SplitterValue`].
385struct SplitThreshValue<E>(core::marker::PhantomData<E>);
386
387impl<E: ScalarValue> SplatConst<E> for SplitThreshValue<E> {
388    const VALUE: E = <E as ScalarValue>::SPLIT_THRESH;
389}
390
391/// `SplatConst` carrier for [`ScalarValue::SPLIT_DOWN`]. See [`SplitterValue`].
392struct SplitDownValue<E>(core::marker::PhantomData<E>);
393
394impl<E: ScalarValue> SplatConst<E> for SplitDownValue<E> {
395    const VALUE: E = <E as ScalarValue>::SPLIT_DOWN;
396}
397
398/// `SplatConst` carrier for [`ScalarValue::SPLIT_UP`]. See [`SplitterValue`].
399struct SplitUpValue<E>(core::marker::PhantomData<E>);
400
401impl<E: ScalarValue> SplatConst<E> for SplitUpValue<E> {
402    const VALUE: E = <E as ScalarValue>::SPLIT_UP;
403}
404
405/// `SplatConst` carrier for [`ScalarValue::MAX_ERFINV_SERIES`]. See [`SplitterValue`].
406struct MaxErfinvSeriesValue<E>(core::marker::PhantomData<E>);
407
408impl<E: ScalarValue> SplatConst<E> for MaxErfinvSeriesValue<E> {
409    const VALUE: E = <E as ScalarValue>::MAX_ERFINV_SERIES;
410}
411
412/// `SplatConst` carrier for [`ScalarValue::ERF_CF_SPLIT`]. See [`SplitterValue`].
413struct ErfCfSplitValue<E>(core::marker::PhantomData<E>);
414
415impl<E: ScalarValue> SplatConst<E> for ErfCfSplitValue<E> {
416    const VALUE: E = <E as ScalarValue>::ERF_CF_SPLIT;
417}
418
419/// Cold half of [`ScalarValue::rebalance_for_split`] for vectors.
420///
421/// Outlined so the hot path neither blends nor spills. The rebalance is per lane, so one
422/// huge value cannot disturb its neighbours, and both factors are exact powers of two, so
423/// lanes under the threshold come out bit-identical to the unguarded path.
424#[cold]
425#[inline(never)]
426fn rebalance_split_cold<R: thermite::register::FloatRegister>(a: Vector<R>, b: Vector<R>) -> (Vector<R>, Vector<R>)
427where
428    R::Element: ScalarValue,
429{
430    let thresh = <Vector<R> as ScalarValue>::SPLIT_THRESH;
431    let down = <Vector<R> as ScalarValue>::SPLIT_DOWN;
432    let up = <Vector<R> as ScalarValue>::SPLIT_UP;
433    let one = <Vector<R> as ScalarValue>::SCALAR_ONE;
434
435    let big_a = a.abs().cmp_gt(thresh);
436    let big_b = b.abs().cmp_gt(thresh);
437
438    let sa = big_a.select(down, big_b.select(up, one));
439    let sb = big_a.select(up, big_b.select(down, one));
440
441    (a * sa, b * sb)
442}
443
444impl<R: thermite::register::FloatRegister> ScalarValue for Vector<R>
445where
446    R::Element: ScalarValue,
447{
448    const SPLITTER: Self = const_splat::<Self, SplitterValue<R::Element>>();
449    const SPLIT_THRESH: Self = const_splat::<Self, SplitThreshValue<R::Element>>();
450    const SPLIT_DOWN: Self = const_splat::<Self, SplitDownValue<R::Element>>();
451    const SPLIT_UP: Self = const_splat::<Self, SplitUpValue<R::Element>>();
452    const SCALAR_ZERO: Self = Self::ZERO;
453    const SCALAR_ONE: Self = Self::ONE;
454    const MAX_ERFINV_SERIES: Self = const_splat::<Self, MaxErfinvSeriesValue<R::Element>>();
455    const ERF_CF_SPLIT: Self = const_splat::<Self, ErfCfSplitValue<R::Element>>();
456
457    // Error-free transformations delegate to core, whose scalar backend overrides them
458    // strict. The trait defaults spell them in `+`/`-`/`*`, which `algebraic-scalar` makes
459    // reassociable on the scalar backend (see the crate-level note).
460    //
461    // `veltkamp_split` is not overridden; its only caller was `two_prod`, which now
462    // delegates. It stays as the reference implementation.
463    #[inline(always)]
464    fn two_sum(a: Self, b: Self) -> (Self, Self) {
465        <Self as FloatVectorWithBits>::two_sum(a, b)
466    }
467
468    #[inline(always)]
469    fn two_diff(a: Self, b: Self) -> (Self, Self) {
470        <Self as FloatVectorWithBits>::two_diff(a, b)
471    }
472
473    #[inline(always)]
474    fn two_prod(a: Self, b: Self) -> (Self, Self) {
475        <Self as FloatVectorWithBits>::two_prod(a, b)
476    }
477
478    #[inline(always)]
479    fn square(a: Self) -> (Self, Self) {
480        <Self as FloatVectorWithBits>::two_square(a)
481    }
482
483    #[inline(always)]
484    fn two_quot(a: Self, b: Self) -> (Self, Self) {
485        <Self as FloatVectorWithBits>::two_quot(a, b)
486    }
487
488    // Operands this large are rare, so the packet takes one predictable branch rather
489    // than four blends on every call. Below SSE4.1 there is no `blendv` and each select
490    // is a three-op polyfill; the rebalance itself also needs enough registers to force
491    // callee-saved spills into the prologue, which the hot path would pay even when the
492    // branch is not taken. Both costs move into `rebalance_split_cold`.
493    //
494    // NOTE: `two_prod` now delegates to core, which uses its own per-lane-select
495    // `rebalance_for_split`, so this one only runs when called directly. Whether the cold
496    // branch was worth it off-FMA (x86_v1/v2, scalar) was never measured.
497    #[inline(always)]
498    fn rebalance_for_split(a: Self, b: Self) -> (Self, Self) {
499        // One test for the whole packet, on the larger of the two magnitudes.
500        if a.abs().max(b.abs()).cmp_gt(Self::SPLIT_THRESH).any() {
501            rebalance_split_cold(a, b)
502        } else {
503            (a, b)
504        }
505    }
506
507    #[inline(always)]
508    fn scalar_trunc(self) -> Self {
509        self.trunc()
510    }
511
512    type CompensatedConstInt<const N: LargeInt> =
513        CompensatedVectorConst<<R::Element as ScalarValue>::CompensatedConstInt<N>>;
514
515    type CompensatedConstRatio<const N: LargeInt, const D: LargeInt> =
516        CompensatedVectorConst<<R::Element as ScalarValue>::CompensatedConstRatio<N, D>>;
517}
518
519// /// NOTE: Nesting Compensated is not recommended. This is only implemented
520// /// for completeness. If you need higher precision, consider using a wider
521// /// base type instead, potentially a `BigFloat` from `thermite-bignum` instead of
522// /// `Compensated` values altogether.
523// impl<V: ScalarValue> ScalarValue for Compensated<V> {
524//     const SPLITTER: Self = const {
525//         assert!(
526//             matches!(V::HAS_NATIVE_FMA, tribool::True),
527//             "Compensated<S> requires true FMA support to implement ScalarValue"
528//         );
529
530//         Self {
531//             value: V::SPLITTER,
532//             error: V::SCALAR_ZERO,
533//         }
534//     };
535
536//     const SCALAR_ZERO: Self = Self {
537//         value: V::SCALAR_ZERO,
538//         error: V::SCALAR_ZERO,
539//     };
540
541//     const SCALAR_ONE: Self = Self {
542//         value: V::SCALAR_ONE,
543//         error: V::SCALAR_ZERO,
544//     };
545
546//     fn scalar_trunc(self) -> Self {
547//         Self {
548//             value: self.value.scalar_trunc(),
549//             error: V::SCALAR_ZERO,
550//         }
551//     }
552// }
553
554/// Trait for float vector types that can be used in compensated arithmetic.
555pub trait CompensatedFloatVector:
556    ScalarValue + FloatVector<Element: ScalarValue> + CastVector<Self> + SwizzleVector
557{
558}
559impl<V> CompensatedFloatVector for V where
560    V: ScalarValue + FloatVector<Element: ScalarValue> + CastVector<V> + SwizzleVector
561{
562}
563
564// Lane swizzles apply to both components: value and error move through the
565// same permutation, so a swizzled compensated number stays a valid
566// (value, error) pair.
567impl<V: CompensatedFloatVector> Swizzle<V::Lanes> for Compensated<V> {
568    #[inline(always)]
569    fn swizzle_const<I: SwizzleIndices<V::Lanes>>(self, other: Self) -> Self {
570        Self {
571            value: self.value.swizzle_const::<I>(other.value),
572            error: self.error.swizzle_const::<I>(other.error),
573        }
574    }
575
576    #[inline(always)]
577    fn permutev_const<I: SwizzleIndices<V::Lanes>>(self) -> Self {
578        Self {
579            value: self.value.permutev_const::<I>(),
580            error: self.error.permutev_const::<I>(),
581        }
582    }
583}
584
585#[rustfmt::skip]
586impl<E: ScalarValue + Element> Element for Compensated<E> {
587    type Signed = <E as Element>::Signed;
588    type Unsigned = <E as Element>::Unsigned;
589
590    const ONE: Self = Self { value: E::ONE, error: E::ZERO };
591    const ZERO: Self = Self { value: E::ZERO, error: E::ZERO };
592
593    // The order extremes carry a zero error term (`inf + 0` is exact), and
594    // unordered values (NaN) exist exactly when the inner type has them.
595    const ORDER_MAX: Self = Self { value: E::ORDER_MAX, error: E::ZERO };
596    const ORDER_MIN: Self = Self { value: E::ORDER_MIN, error: E::ZERO };
597    const HAS_UNORDERED: bool = E::HAS_UNORDERED;
598    const IS_FLOAT: bool = E::IS_FLOAT;
599
600    fn from_i8(value: i8) -> Self { Self { value: E::from_i8(value), error: E::ZERO } }
601    fn from_u8(value: u8) -> Self { Self { value: E::from_u8(value), error: E::ZERO } }
602    fn from_u16(value: u16) -> Self { Self { value: E::from_u16(value), error: E::ZERO } }
603}
604
605#[rustfmt::skip]
606impl<E: ScalarValue + SignedElement> SignedElement for Compensated<E> {
607    #[inline(always)]
608    fn abs(self) -> Self {
609        if self.value() < E::ZERO {
610            -self
611        } else {
612            self
613        }
614    }
615
616    #[inline(always)]
617    fn signum(self) -> Self {
618        Self::new(self.value().signum())
619    }
620}
621
622use core::marker::PhantomData;
623
624// EFT two_sum: returns (s, e) such that s + e = a + b exactly, s = fl(a + b).
625const fn two_sum_f64(a: f64, b: f64) -> (f64, f64) {
626    let s = a + b;
627    let v = s - a;
628    let e = (a - (s - v)) + (b - v);
629    (s, e)
630}
631
632// EFT two_product via Dekker splitting: returns (p, e) such that p + e = a * b exactly,
633// p = fl(a * b). Requires no FMA; accurate when |a|, |b| < 2^996 (no overflow in split).
634//
635// Deliberately UNGUARDED, unlike `ScalarValue::two_prod`. Its only caller is the
636// `N/D` rational-constant carrier below, whose operands are structurally bounded:
637// `frac = r/D` with `r = N % D`, so `|frac| < 1`, and `D: LargeInt` (i64), so
638// `|D as f64| <= 9.3e18`. Both are ~280 orders of magnitude under the 1.34e300 threshold,
639// so `rebalance_for_split` could never fire here. It is also const-evaluated, so a guard
640// would cost nothing and do nothing. Do not "fix" this to match `two_prod`.
641const fn two_product_f64(a: f64, b: f64) -> (f64, f64) {
642    let p = a * b;
643    let c = f64::SPLITTER * a;
644    let a_hi = c - (c - a);
645    let a_lo = a - a_hi;
646    let c = f64::SPLITTER * b;
647    let b_hi = c - (c - b);
648    let b_lo = b - b_hi;
649    let e = ((a_hi * b_hi - p) + a_hi * b_lo + a_lo * b_hi) + a_lo * b_lo;
650    (p, e)
651}
652
653// --- f32: uses f64 intermediate to capture rounding error in the error term ---
654
655#[doc(hidden)]
656pub struct F32CompensatedIntConst<const N: LargeInt>;
657#[doc(hidden)]
658pub struct F32CompensatedRatioConst<const N: LargeInt, const D: LargeInt>;
659
660impl<const N: LargeInt> SplatConst<Compensated<f32>> for F32CompensatedIntConst<N> {
661    const VALUE: Compensated<f32> = {
662        let value = N as f32;
663        let error = (N as f64 - value as f64) as f32;
664        Compensated { value, error }
665    };
666}
667
668impl<const N: LargeInt, const D: LargeInt> SplatConst<Compensated<f32>> for F32CompensatedRatioConst<N, D> {
669    const VALUE: Compensated<f32> = {
670        assert!(D != 0, "CompensatedRatioConst: denominator must not be zero");
671        let (q, r) = (N / D, N % D);
672        let hi64 = (q as f64) + (r as f64) / (D as f64);
673        let value = hi64 as f32;
674        let error = (hi64 - value as f64) as f32;
675        Compensated { value, error }
676    };
677}
678
679// --- f64: double-double EFT to capture the rounding error without needing f128 ---
680
681#[doc(hidden)]
682pub struct F64CompensatedIntConst<const N: LargeInt>;
683#[doc(hidden)]
684pub struct F64CompensatedRatioConst<const N: LargeInt, const D: LargeInt>;
685
686impl<const N: LargeInt> SplatConst<Compensated<f64>> for F64CompensatedIntConst<N> {
687    const VALUE: Compensated<f64> = {
688        // If |N| ≤ 2^53 the cast is exact, so error = 0. Otherwise the rounding error
689        // is an integer ≤ ulp(value)/2, which is always exactly representable in f64.
690        let value = N as f64;
691        let error = (N - value as LargeInt) as f64;
692        Compensated { value, error }
693    };
694}
695
696impl<const N: LargeInt, const D: LargeInt> SplatConst<Compensated<f64>> for F64CompensatedRatioConst<N, D> {
697    const VALUE: Compensated<f64> = {
698        assert!(D != 0, "CompensatedRatioConst: denominator must not be zero");
699        let (q, r) = (N / D, N % D);
700        let q_f64 = q as f64;
701        let r_f64 = r as f64;
702        let d_f64 = D as f64;
703
704        // frac = fl(r / D), with rounding error frac_err = r/D - frac.
705        let frac = r_f64 / d_f64;
706
707        // value = fl(q + frac); two_sum gives us the exact rounding error e_add.
708        // q_f64 + frac = value + e_add (exactly).
709        let (value, e_add) = two_sum_f64(q_f64, frac);
710
711        // Recover frac * D exactly via Dekker two_product, so we can compute
712        // r - frac*D = (r/D - frac)*D, the numerator of the division error.
713        let (prod, e_prod) = two_product_f64(frac, d_f64);
714
715        // r - frac*D = r_f64 - prod - e_prod. Compute (r_f64 - prod) with two_sum
716        // to avoid cancellation, then fold in e_prod.
717        let (diff, e_diff) = two_sum_f64(r_f64, -prod);
718        let frac_err = (diff + (e_diff - e_prod)) / d_f64;
719
720        // Total: value + error = q + r/D = N/D (to full double-double precision,
721        // exact when |q|, |r|, |D| each fit in 2^53).
722        let error = frac_err + e_add;
723
724        Compensated { value, error }
725    };
726}
727
728// --- Vector: lifts a scalar SplatConst<Compensated<V::Element>> to SplatConst<Compensated<V>> ---
729// Delegates to the existing VectorValue impl which splats value and error independently.
730
731#[doc(hidden)]
732pub struct CompensatedVectorConst<Inner>(PhantomData<Inner>);
733
734// --- New (per-lane values) support for Compensated<V> ---
735
736#[doc(hidden)]
737pub struct CompensatedNewImpl;
738
739struct CompensatedValueConst<C, V>(PhantomData<(C, V)>);
740struct CompensatedErrorConst<C, V>(PhantomData<(C, V)>);
741
742impl<C, V: CompensatedFloatVector> NewConst<V::Element, V::Lanes> for CompensatedValueConst<C, V>
743where
744    C: NewConst<Compensated<V::Element>, V::Lanes>,
745{
746    const VALUES: GenericArray<V::Element, V::Lanes> = const {
747        let c_vals = C::VALUES;
748        let src = c_vals.as_slice();
749        let mut out: GenericArray<V::Element, V::Lanes> = unsafe { core::mem::zeroed() };
750        let dst = out.as_mut_slice();
751        let mut i = 0;
752        while i < V::LANES {
753            dst[i] = src[i].value;
754            i += 1;
755        }
756        core::mem::forget(c_vals);
757        out
758    };
759}
760
761impl<C, V: CompensatedFloatVector> NewConst<V::Element, V::Lanes> for CompensatedErrorConst<C, V>
762where
763    C: NewConst<Compensated<V::Element>, V::Lanes>,
764{
765    const VALUES: GenericArray<V::Element, V::Lanes> = const {
766        let c_vals = C::VALUES;
767        let src = c_vals.as_slice();
768        let mut out: GenericArray<V::Element, V::Lanes> = unsafe { core::mem::zeroed() };
769        let dst = out.as_mut_slice();
770        let mut i = 0;
771        while i < V::LANES {
772            dst[i] = src[i].error;
773            i += 1;
774        }
775        core::mem::forget(c_vals);
776        out
777    };
778}
779
780impl<T, V: CompensatedFloatVector> VectorValue<T, Compensated<V>> for CompensatedNewImpl
781where
782    T: NewConst<Compensated<V::Element>, V::Lanes>,
783{
784    const VALUE: Compensated<V> = Compensated {
785        value: <<V as NewVector<V::Element, V::Lanes>>::New<CompensatedValueConst<T, V>> as VectorValue<
786            CompensatedValueConst<T, V>,
787            V,
788        >>::VALUE,
789        error: <<V as NewVector<V::Element, V::Lanes>>::New<CompensatedErrorConst<T, V>> as VectorValue<
790            CompensatedErrorConst<T, V>,
791            V,
792        >>::VALUE,
793    };
794}
795
796impl<V: CompensatedFloatVector> NewVector<Compensated<V::Element>, V::Lanes> for Compensated<V> {
797    type New<T: NewConst<Compensated<V::Element>, V::Lanes>> = CompensatedNewImpl;
798}
799
800impl<V, Inner> SplatConst<Compensated<V>> for CompensatedVectorConst<Inner>
801where
802    V: CompensatedFloatVector,
803    Inner: SplatConst<Compensated<V::Element>>,
804{
805    const VALUE: Compensated<V> = <Compensated<V> as VectorValue<Inner, Compensated<V>>>::VALUE;
806}
807
808#[rustfmt::skip]
809impl<E: ScalarValue + FloatElement> FloatElement for Compensated<E> {
810    #[inline(always)]
811    fn sqrt(this: Self) -> Self {
812        let s = E::sqrt(this.value);
813
814        let (p, e) = E::square(s);
815
816        // Strict subtractions, same as the `FloatVector` twin below. Redundant when `E`
817        // is a plain element, necessary otherwise.
818        let (d_value, _) = E::two_diff(this.value, p);
819        let (d_error, _) = E::two_diff(this.error, e);
820        let remainder = d_value + d_error;
821
822        // correction term
823        let corr = remainder / (s + s);
824
825        Self::renormalized(s, corr)
826    }
827
828    #[inline(always)] fn floor(this: Self) -> Self { Self::new(E::floor(this.value())) }
829    #[inline(always)] fn ceil(this: Self) -> Self { Self::new(E::ceil(this.value())) }
830    #[inline(always)] fn round(this: Self) -> Self { Self::new(E::round(this.value())) }
831    #[inline(always)] fn trunc(this: Self) -> Self { Self::new(E::trunc(this.value())) }
832
833    // for these two, we rely on `renormalized` to avoid infinite error values
834    #[inline(always)] fn next_up(this: Self) -> Self { Self::renormalized(this.value, E::next_up(this.error)) }
835    #[inline(always)] fn next_down(this: Self) -> Self { Self::renormalized(this.value, E::next_down(this.error)) }
836
837    // TODO: Represent these more accurately
838    #[inline(always)]
839    fn try_from_int(value: LargeInt) -> Option<Self> {
840        E::try_from_int(value).map(|v| Self::new(v))
841    }
842
843    #[inline(always)]
844    fn try_from_ratio(n: LargeInt, d: LargeInt) -> Option<Self> {
845        if d == 0 {
846            return None;
847        }
848
849        let df = <E as FloatElement>::try_from_int(d)?;
850
851        // fast path for values that both fit in the float exactly
852        if let Some(n) = <E as FloatElement>::try_from_int(n) {
853            return Some(Self::from_fraction(n, df));
854        }
855
856        let (q, r) = (n / d, n % d);
857
858        let mut result = Self::try_from_int(q)?;
859
860        if r != 0 {
861            let rf = <E as FloatElement>::try_from_int(r)?;
862
863            result += Self::from_fraction(rf, df);
864        }
865
866        Some(result)
867    }
868
869    const HAS_INFINITY: bool = E::HAS_INFINITY;
870    const HAS_SIGNED_ZERO: bool = E::HAS_SIGNED_ZERO;
871    const HAS_SUBNORMALS: bool = E::HAS_SUBNORMALS;
872
873    type ConstInt<const N: thermite::LargeInt> = E::CompensatedConstInt<N>;
874
875    type ConstRatio<const N: thermite::LargeInt, const D: thermite::LargeInt> = E::CompensatedConstRatio<N, D>;
876}
877
878/// Compensated arithmetic number type.
879///
880/// This type represents a number as the sum of two components: a high-order value and a low-order error term.
881/// Using these, it can effectively double the mantissa precision of standard floating-point types,
882/// providing significantly improved accuracy for a wide range of numerical computations.
883///
884/// `Compensated<f32 | f64>` have some functionality required for use as an `Element`
885/// in vectorized types, but cannot use the math library. Use `Vector<f32>` or `Vector<f64>`
886/// as the inner type for full functionality.
887#[derive(Debug, Default, Clone, Copy, PartialEq, PartialOrd)]
888#[repr(C)]
889pub struct Compensated<V> {
890    pub value: V,
891    pub error: V,
892}
893
894impl<V: ScalarValue> thermite::const_default::ConstDefault for Compensated<V> {
895    const DEFAULT: Self = Compensated {
896        value: V::SCALAR_ZERO,
897        error: V::SCALAR_ZERO,
898    };
899}
900
901impl<V: ScalarValue> Compensated<V> {
902    /// Creates a new compensated number with zero error term.
903    #[inline(always)]
904    pub const fn new(value: V) -> Self {
905        Self {
906            value,
907            error: V::SCALAR_ZERO,
908        }
909    }
910
911    /// Returns the normalized value `(value + error)`.
912    ///
913    /// Strict, so a re-bracketing into surrounding arithmetic cannot discard the second
914    /// word instead of rounding it in.
915    #[inline(always)]
916    pub fn value(self) -> V {
917        V::two_sum(self.value, self.error).0
918    }
919
920    /// Returns the uncompensated value, with no error term applied.
921    #[inline(always)]
922    pub const fn uncompensated(self) -> V {
923        self.value
924    }
925
926    /// Returns the error term.
927    #[inline(always)]
928    pub const fn error(self) -> V {
929        self.error
930    }
931
932    /// Renormalizes a compensated number from a value and error term.
933    ///
934    /// Goes through `two_sum` rather than spelling Fast2Sum inline, since the inline form
935    /// is algebraically zero and `algebraic-scalar` may fold it. Knuth's unconditional
936    /// form, because `|value| >= |error|` is what this function is meant to restore, not
937    /// something it can assume.
938    #[inline(always)]
939    pub(crate) fn renormalized(value: V, error: V) -> Self {
940        let (sum, err) = V::two_sum(value, error);
941        Self { value: sum, error: err }
942    }
943
944    #[inline(always)]
945    pub fn normalize(self) -> Self {
946        Self::renormalized(self.value, self.error)
947    }
948}
949
950impl<V: CompensatedFloatVector> Compensated<V> {
951    pub fn splat_value(value: V::Element) -> Self {
952        Self {
953            value: V::splat(value),
954            error: V::ZERO,
955        }
956    }
957}
958
959// for testing
960const ALLOW_UNNORMALIZED: bool = true;
961
962impl<V: ScalarValue> Compensated<V> {
963    /// Accumulate rhs into self without renormalization.
964    ///
965    /// This should only be used in specific scenarios where renormalization is not desired,
966    /// such as within iterative series expansions.
967    #[inline(always)]
968    pub fn accumulate_unnormalized(&mut self, rhs: Self) {
969        if ALLOW_UNNORMALIZED {
970            let (s, e) = V::two_sum(self.value, rhs.value);
971            self.value = s;
972            // Strict. The two per-step residuals combine before meeting the running error.
973            let (t, _) = V::two_sum(e, rhs.error);
974            let (t, _) = V::two_sum(self.error, t);
975            self.error = t;
976        } else {
977            *self += rhs;
978        }
979    }
980
981    /// Reduce rhs from self without renormalization.
982    ///
983    /// This should only be used in specific scenarios where renormalization is not desired,
984    /// such as within iterative series expansions.
985    #[inline(always)]
986    pub fn reduce_unnormalized(&mut self, rhs: Self) {
987        if ALLOW_UNNORMALIZED {
988            let (s, e) = V::two_diff(self.value, rhs.value);
989            self.value = s;
990            // `self.error - rhs.error` cancels and carries the result. Strict, see `Sub`.
991            let (d, _) = V::two_diff(self.error, rhs.error);
992            let (t, _) = V::two_sum(e, d);
993            self.error = t;
994        } else {
995            *self -= rhs;
996        }
997    }
998}
999
1000#[rustfmt::skip]
1001impl<V: ScalarValue> Neg for Compensated<V> {
1002    type Output = Self;
1003
1004    #[inline(always)]
1005    fn neg(self) -> Self::Output {
1006        Self { value: -self.value, error: -self.error }
1007    }
1008}
1009
1010impl<V: ScalarValue> Add<Self> for Compensated<V> {
1011    type Output = Self;
1012
1013    #[inline(always)]
1014    fn add(self, rhs: Self) -> Self::Output {
1015        let (s, e) = V::two_sum(self.value, rhs.value);
1016
1017        // Strict, left-associated as before. Opposite-sign error words cancel here too.
1018        let (t, _) = V::two_sum(e, self.error);
1019        let (t, _) = V::two_sum(t, rhs.error);
1020
1021        Self::renormalized(s, t)
1022    }
1023}
1024
1025impl<V: ScalarValue> Add<V> for Compensated<V> {
1026    type Output = Self;
1027
1028    #[inline(always)]
1029    fn add(self, rhs: V) -> Self::Output {
1030        let (s, e) = V::two_sum(self.value, rhs);
1031        let (t, _) = V::two_sum(e, self.error);
1032        Self::renormalized(s, t)
1033    }
1034}
1035
1036impl<V: ScalarValue> Sub<Self> for Compensated<V> {
1037    type Output = Self;
1038
1039    #[inline(always)]
1040    fn sub(self, rhs: Self) -> Self::Output {
1041        let (s, e) = V::two_diff(self.value, rhs.value);
1042
1043        // `self.error - rhs.error` must be strict: when the values nearly cancel so do the
1044        // error words, and this difference IS the second word of the result. As a bare `-`
1045        // under `algebraic-scalar` it folded, dropping `digamma`/`trigamma` to plain f64
1046        // (5.5e-17 relative). The outer `e + d` was measured and does not need this.
1047        let (d, _) = V::two_diff(self.error, rhs.error);
1048
1049        Self::renormalized(s, e + d)
1050    }
1051}
1052
1053#[allow(clippy::suspicious_arithmetic_impl)]
1054impl<V: ScalarValue> Sub<V> for Compensated<V> {
1055    type Output = Self;
1056
1057    #[inline(always)]
1058    fn sub(self, rhs: V) -> Self::Output {
1059        let (s, e) = V::two_diff(self.value, rhs);
1060        let (t, _) = V::two_sum(e, self.error);
1061        Self::renormalized(s, t)
1062    }
1063}
1064
1065impl<V: ScalarValue> Square for Compensated<V> {
1066    type Output = Self;
1067
1068    #[inline(always)]
1069    fn square(self) -> Self {
1070        let (p, e) = V::square(self.value);
1071
1072        // `d + d` is exact, but `contract` would otherwise be free to fuse the product
1073        // into the following add and change the rounding, so take the product strictly too.
1074        let d = V::two_prod(self.error, self.value).0;
1075        let (dd, _) = V::two_sum(d, d);
1076        let (t, _) = V::two_sum(dd, e);
1077
1078        Self::renormalized(p, t)
1079    }
1080}
1081
1082impl<V: CompensatedFloatVector> SquareMasked<V::Mask> for Compensated<V> {
1083    #[inline(always)]
1084    fn square_c(self, mask: V::Mask) -> Self::Output {
1085        mask.select(self.square(), self)
1086    }
1087
1088    #[inline(always)]
1089    fn square_m(self, src: Self, mask: V::Mask) -> Self::Output {
1090        mask.select(self.square(), src)
1091    }
1092
1093    #[inline(always)]
1094    fn square_z(self, mask: V::Mask) -> Self::Output {
1095        mask.select(self.square(), Self::ZERO)
1096    }
1097}
1098
1099impl<V: ScalarValue> Mul<Self> for Compensated<V> {
1100    type Output = Self;
1101
1102    #[inline(always)]
1103    fn mul(self, rhs: Self) -> Self::Output {
1104        let (p, e) = V::two_prod(self.value, rhs.value);
1105
1106        let e = self.error.mul_adde(rhs.value, self.value.mul_adde(rhs.error, e));
1107
1108        Self::renormalized(p, e)
1109    }
1110}
1111
1112impl<V: ScalarValue> Mul<V> for Compensated<V> {
1113    type Output = Self;
1114
1115    #[inline(always)]
1116    fn mul(self, rhs: V) -> Self {
1117        // (a0 + a1) * b = a0*b + a1*b
1118        let (p, e1) = V::two_prod(self.value, rhs);
1119        // We just add a1*b to the error term
1120        Self::renormalized(p, self.error.mul_adde(rhs, e1))
1121    }
1122}
1123
1124impl<V: ScalarValue> Div<Self> for Compensated<V> {
1125    type Output = Self;
1126
1127    #[inline(always)]
1128    fn div(self, rhs: Self) -> Self {
1129        let q1 = V::two_quot(self.value, rhs.value).0;
1130
1131        let (p_hi, p_lo) = V::two_prod(q1, rhs.value);
1132
1133        // calculate the remainder r
1134        // let r = (self.value - p_hi) - p_lo + self.error - (q1 * rhs.error);
1135        //
1136        // Strict join: the remainder has cancelled and this add carries the result.
1137        // `nmul_adde` only covers its own half.
1138        let (r, _) = V::two_sum(
1139            division_remainder(self.value, p_hi, p_lo),
1140            q1.nmul_adde(rhs.error, self.error),
1141        );
1142
1143        Self::renormalized(q1, r / rhs.value)
1144    }
1145}
1146
1147impl<V: ScalarValue> Compensated<V> {
1148    pub fn div_scalar(num: V, denom: Self) -> Self {
1149        let q1 = V::two_quot(num, denom.value).0;
1150
1151        let (p_hi, p_lo) = V::two_prod(q1, denom.value);
1152
1153        // calculate the remainder r
1154        // let r = (self.value - p_hi) - p_lo + self.error - (q1 * rhs.error);
1155        let (r, _) = V::two_diff(division_remainder(num, p_hi, p_lo), V::two_prod(q1, denom.error).0);
1156
1157        Compensated::renormalized(q1, r / denom.value)
1158    }
1159}
1160
1161impl<V: ScalarValue> Compensated<V> {
1162    /// Creates a compensated number from a fraction `numerator / denominator`,
1163    /// dividing with compensation.
1164    #[inline(always)]
1165    pub fn from_fraction(numerator: V, denominator: V) -> Self {
1166        let q1 = V::two_quot(numerator, denominator).0;
1167
1168        let (p_hi, p_lo) = V::two_prod(q1, denominator);
1169
1170        // calculate the remainder r
1171        let r = division_remainder(numerator, p_hi, p_lo);
1172
1173        Self::renormalized(q1, r / denominator)
1174    }
1175}
1176
1177impl Compensated<f32> {
1178    /// Create a compensated f32 value from an f64 value,
1179    /// preserving as much precision as possible.
1180    #[inline(always)]
1181    pub const fn from_f64(v: f64) -> Self {
1182        let v_f32 = v as f32;
1183        let err = v - (v_f32 as f64);
1184        Self {
1185            value: v_f32,
1186            error: err as f32,
1187        }
1188    }
1189}
1190
1191/// `(x - p_hi) - p_lo`, the remainder step shared by every compensated division, with
1192/// both subtractions strict.
1193///
1194/// The order matters: `x - p_hi` cancels almost completely, and the second step corrects
1195/// at `p_lo`'s scale. Re-bracketed as `x - (p_hi + p_lo)` the product rounds back to one
1196/// word and the remainder collapses, which is exactly what `algebraic-scalar` did to
1197/// `Compensated / V` (error term came back zero).
1198///
1199/// The residuals are discarded to match what the call sites did before. Carrying them
1200/// would be more accurate and is a separate change.
1201#[inline(always)]
1202fn division_remainder<V: ScalarValue>(x: V, p_hi: V, p_lo: V) -> V {
1203    let (t, _) = V::two_diff(x, p_hi);
1204    let (t, _) = V::two_diff(t, p_lo);
1205    t
1206}
1207
1208impl<V: ScalarValue> Div<V> for Compensated<V> {
1209    type Output = Self;
1210
1211    #[inline(always)]
1212    fn div(self, rhs: V) -> Self {
1213        // same as regular division, but rhs has no error term
1214        let q1 = V::two_quot(self.value, rhs).0;
1215
1216        let (p_hi, p_lo) = V::two_prod(q1, rhs);
1217
1218        // calculate the remainder r
1219        //
1220        // The `+ self.error` must be strict too: the remainder has cancelled, so this add
1221        // carries the whole result. `Div<Self>` gets that from `nmul_adde` for free.
1222        let (r, _) = V::two_sum(division_remainder(self.value, p_hi, p_lo), self.error);
1223
1224        Self::renormalized(q1, r / rhs)
1225    }
1226}
1227
1228impl<V: ScalarValue> Rem<Self> for Compensated<V> {
1229    type Output = Self;
1230
1231    #[inline(always)]
1232    fn rem(self, rhs: Self) -> Self {
1233        let q = self / rhs;
1234        let n = Compensated::new(-q.value.scalar_trunc());
1235        rhs.mul_add(n, self)
1236    }
1237}
1238
1239impl<V: ScalarValue> Rem<V> for Compensated<V> {
1240    type Output = Self;
1241
1242    #[inline(always)]
1243    fn rem(self, rhs: V) -> Self {
1244        let q = self / rhs;
1245        let n = Compensated::new(-q.value.scalar_trunc());
1246        MulAddExt::mul_add(n, rhs, self)
1247    }
1248}
1249
1250#[rustfmt::skip]
1251impl<V: ScalarValue> MulAddExt<Self, Self> for Compensated<V> {
1252    type Output = Self;
1253
1254    // Compensated mul-add is always accurate, and have the same code paths,
1255    // so we can just set this to true.
1256    const HAS_NATIVE_FMA: Tribool = tribool::True;
1257
1258    #[inline(always)]
1259    fn mul_add(self, b: Self, c: Self) -> Self {
1260        let (p, e_prod_base) = V::two_prod(self.value, b.value);
1261        let (s, e_sum) = V::two_sum(p, c.value);
1262
1263        let e_prod = self.error.mul_adde(b.value, self.value.mul_adde(b.error, V::two_sum(e_prod_base, e_sum).0));
1264
1265        Self::renormalized(s, V::two_sum(e_prod, c.error).0)
1266    }
1267
1268    #[inline(always)]
1269    fn mul_sub(self, b: Self, c: Self) -> Self::Output {
1270        let (p, e_prod_base) = V::two_prod(self.value, b.value);
1271        let (s, e_diff) = V::two_diff(p, c.value);
1272
1273        let e_prod = self.error.mul_adde(b.value, self.value.mul_adde(b.error, V::two_sum(e_prod_base, e_diff).0));
1274
1275        // Subtract c.error because the operation is (a*b) - c
1276        // The total error is the product error + subtraction error - c's error component
1277        Self::renormalized(s, V::two_diff(e_prod, c.error).0)
1278    }
1279
1280    #[inline(always)] fn nmul_add(self, a: Self, b: Self) -> Self::Output { self.mul_add(-a, b) }
1281    #[inline(always)] fn nmul_sub(self, a: Self, b: Self) -> Self::Output { self.mul_sub(-a, b) }
1282    #[inline(always)] fn mul_adde(self, a: Self, b: Self) -> Self::Output { self.mul_add(a, b) }
1283    #[inline(always)] fn mul_sube(self, a: Self, b: Self) -> Self::Output { self.mul_sub(a, b) }
1284    #[inline(always)] fn nmul_adde(self, a: Self, b: Self) -> Self::Output { self.nmul_add(a, b) }
1285    #[inline(always)] fn nmul_sube(self, a: Self, b: Self) -> Self::Output { self.nmul_sub(a, b) }
1286}
1287
1288#[rustfmt::skip]
1289impl<V: ScalarValue> MulAddExt<V, Self> for Compensated<V> {
1290    type Output = Self;
1291
1292    const HAS_NATIVE_FMA: Tribool = tribool::True;
1293
1294    #[inline(always)]
1295    fn mul_add(self, b: V, c: Self) -> Self::Output {
1296        let (p, e_prod_base) = V::two_prod(self.value, b);
1297        let (s, e_sum) = V::two_sum(p, c.value);
1298
1299        let e_prod = self.error.mul_adde(b, V::two_sum(e_prod_base, e_sum).0);
1300
1301        Self::renormalized(s, V::two_sum(e_prod, c.error).0)
1302    }
1303
1304    #[inline(always)]
1305    fn mul_sub(self, b: V, c: Self) -> Self::Output {
1306        let (p, e_prod_base) = V::two_prod(self.value, b);
1307        let (s, e_diff) = V::two_diff(p, c.value);
1308
1309        let e_prod = self.error.mul_adde(b, V::two_sum(e_prod_base, e_diff).0);
1310
1311        Self::renormalized(s, V::two_diff(e_prod, c.error).0)
1312    }
1313
1314    #[inline(always)] fn nmul_add(self, a: V, b: Self) -> Self::Output { self.mul_add(-a, b) }
1315    #[inline(always)] fn nmul_sub(self, a: V, b: Self) -> Self::Output { self.mul_sub(-a, b) }
1316    #[inline(always)] fn mul_adde(self, a: V, b: Self) -> Self::Output { self.mul_add(a, b) }
1317    #[inline(always)] fn mul_sube(self, a: V, b: Self) -> Self::Output { self.mul_sub(a, b) }
1318    #[inline(always)] fn nmul_adde(self, a: V, b: Self) -> Self::Output { self.nmul_add(a, b) }
1319    #[inline(always)] fn nmul_sube(self, a: V, b: Self) -> Self::Output { self.nmul_sub(a, b) }
1320}
1321
1322#[rustfmt::skip]
1323impl<V: ScalarValue> MulAddExt<Self, V> for Compensated<V> {
1324    type Output = Self;
1325
1326    const HAS_NATIVE_FMA: Tribool = tribool::True;
1327
1328    #[inline(always)]
1329    fn mul_add(self, a: Self, b: V) -> Self::Output {
1330        let (p, e_prod_base) = V::two_prod(self.value, a.value);
1331        let (s, e_sum) = V::two_sum(p, b);
1332
1333        // Estimating FMAs, like the sibling impls: this is the second-order error term,
1334        // where a rounding is already below the result's last bit, so it is not worth an
1335        // emulated FMA on a backend without one.
1336        let e_prod = self.error.mul_adde(a.value, self.value.mul_adde(a.error, V::two_sum(e_prod_base, e_sum).0));
1337
1338        Self::renormalized(s, e_prod)
1339    }
1340
1341    #[inline(always)]
1342    fn mul_sub(self, b: Self, c: V) -> Self::Output {
1343        let (p, e_prod_base) = V::two_prod(self.value, b.value);
1344        let (s, e_diff) = V::two_diff(p, c);
1345
1346        let e_prod = self.error.mul_adde(b.value, self.value.mul_adde(b.error, V::two_sum(e_prod_base, e_diff).0));
1347
1348        Self::renormalized(s, e_prod)
1349    }
1350
1351    #[inline(always)] fn nmul_add(self, a: Self, b: V) -> Self::Output { self.mul_add(-a, b) }
1352    #[inline(always)] fn nmul_sub(self, a: Self, b: V) -> Self::Output { self.mul_sub(-a, b) }
1353    #[inline(always)] fn mul_adde(self, a: Self, b: V) -> Self::Output { self.mul_add(a, b) }
1354    #[inline(always)] fn mul_sube(self, a: Self, b: V) -> Self::Output { self.mul_sub(a, b) }
1355    #[inline(always)] fn nmul_adde(self, a: Self, b: V) -> Self::Output { self.nmul_add(a, b) }
1356    #[inline(always)] fn nmul_sube(self, a: Self, b: V) -> Self::Output { self.nmul_sub(a, b) }
1357}
1358
1359impl<V: Copy, T> AddAssign<T> for Compensated<V>
1360where
1361    Self: Add<T, Output = Self>,
1362{
1363    #[inline(always)]
1364    fn add_assign(&mut self, rhs: T) {
1365        *self = *self + rhs;
1366    }
1367}
1368
1369impl<V: Copy, T> SubAssign<T> for Compensated<V>
1370where
1371    Self: Sub<T, Output = Self>,
1372{
1373    #[inline(always)]
1374    fn sub_assign(&mut self, rhs: T) {
1375        *self = *self - rhs;
1376    }
1377}
1378
1379impl<V: Copy, T> MulAssign<T> for Compensated<V>
1380where
1381    Self: Mul<T, Output = Self>,
1382{
1383    #[inline(always)]
1384    fn mul_assign(&mut self, rhs: T) {
1385        *self = *self * rhs;
1386    }
1387}
1388
1389impl<V: Copy, T> DivAssign<T> for Compensated<V>
1390where
1391    Self: Div<T, Output = Self>,
1392{
1393    #[inline(always)]
1394    fn div_assign(&mut self, rhs: T) {
1395        *self = *self / rhs;
1396    }
1397}
1398
1399impl<V: Copy, T> RemAssign<T> for Compensated<V>
1400where
1401    Self: Rem<T, Output = Self>,
1402{
1403    #[inline(always)]
1404    fn rem_assign(&mut self, rhs: T) {
1405        *self = *self % rhs;
1406    }
1407}
1408
1409#[rustfmt::skip]
1410impl<V: Copy, A, B> MulAddAssignExt<A, B> for Compensated<V>
1411where
1412    Self: MulAddExt<A, B, Output = Self>,
1413{
1414    #[inline(always)] fn mul_add_assign(&mut self, a: A, b: B) { *self = self.mul_add(a, b); }
1415    #[inline(always)] fn mul_sub_assign(&mut self, a: A, b: B) { *self = self.mul_sub(a, b); }
1416    #[inline(always)] fn nmul_add_assign(&mut self, a: A, b: B) { *self = self.nmul_add(a, b); }
1417    #[inline(always)] fn nmul_sub_assign(&mut self, a: A, b: B) { *self = self.nmul_sub(a, b); }
1418    #[inline(always)] fn mul_adde_assign(&mut self, a: A, b: B) { *self = self.mul_adde(a, b); }
1419    #[inline(always)] fn mul_sube_assign(&mut self, a: A, b: B) { *self = self.mul_sube(a, b); }
1420    #[inline(always)] fn nmul_adde_assign(&mut self, a: A, b: B) { *self = self.nmul_adde(a, b); }
1421    #[inline(always)] fn nmul_sube_assign(&mut self, a: A, b: B) { *self = self.nmul_sube(a, b); }
1422}
1423
1424macro_rules! impl_masked {
1425    (MUL_ADD: $($method:ident),*) => {paste::paste! {
1426        impl<V: CompensatedFloatVector, A, B> thermite::vector::ops::MulAddExtMasked<V::Mask, A, B> for Compensated<V>
1427        where
1428            Compensated<V>: MulAddExt<A, B, Output = Self>,
1429        {
1430            $(
1431                #[inline(always)]
1432                fn [<$method _c>](self, mask: V::Mask, a: A, b: B) -> Self {
1433                    mask.select(self.[<$method>](a, b), self)
1434                }
1435
1436                #[inline(always)]
1437                fn [<$method _m>](self, src: Self, mask: V::Mask, a: A, b: B) -> Self {
1438                    mask.select(self.[<$method>](a, b), src)
1439                }
1440
1441                #[inline(always)]
1442                fn [<$method _z>](self, mask: V::Mask, a: A, b: B) -> Self {
1443                    mask.select(self.[<$method>](a, b), Self::EMPTY)
1444                }
1445            )*
1446        }
1447
1448        impl<V: CompensatedFloatVector, A, B> thermite::vector::ops::MulAddAssignExtMasked<V::Mask, A, B> for Compensated<V>
1449        where
1450            Compensated<V>: MulAddExt<A, B, Output = Self>,
1451        {
1452            $(
1453                #[inline(always)]
1454                fn [<$method _assign_c>](&mut self, mask: V::Mask, a: A, b: B) {
1455                    *self = mask.select(self.[<$method>](a, b), *self);
1456                }
1457
1458                #[inline(always)]
1459                fn [<$method _assign_m>](&mut self, src: Self, mask: V::Mask, a: A, b: B) {
1460                    *self = mask.select(self.[<$method>](a, b), src);
1461                }
1462
1463                #[inline(always)]
1464                fn [<$method _assign_z>](&mut self, mask: V::Mask, a: A, b: B) {
1465                    *self = mask.select(self.[<$method>](a, b), Self::EMPTY);
1466                }
1467            )*
1468        }
1469    }};
1470
1471    ($trait:ident::$method:ident) => {paste::paste! {
1472        impl<V: CompensatedFloatVector, Rhs> thermite::vector::ops::[<$trait Masked>]<V::Mask, Rhs> for Compensated<V>
1473        where
1474            Compensated<V>: $trait<Rhs, Output = Self>,
1475        {
1476            #[inline(always)]
1477            fn [<$method _c>](self, mask: V::Mask, rhs: Rhs) -> Self {
1478                mask.select(self.$method(rhs), self)
1479            }
1480
1481            #[inline(always)]
1482            fn [<$method _m>](self, src: Self, mask: V::Mask, rhs: Rhs) -> Self {
1483                mask.select(self.$method(rhs), src)
1484            }
1485
1486            #[inline(always)]
1487            fn [<$method _z>](self, mask: V::Mask, rhs: Rhs) -> Self {
1488                mask.select(self.$method(rhs), Self::EMPTY)
1489            }
1490        }
1491
1492        impl<V: CompensatedFloatVector, Rhs> thermite::vector::ops::[<$trait AssignMasked>]<V::Mask, Rhs> for Compensated<V>
1493        where
1494            Compensated<V>: $trait<Rhs, Output = Self>,
1495        {
1496            #[inline(always)]
1497            fn [<$method _assign_c>](&mut self, mask: V::Mask, rhs: Rhs) {
1498                *self = mask.select(self.$method(rhs), *self);
1499            }
1500
1501            #[inline(always)]
1502            fn [<$method _assign_m>](&mut self, src: Self, mask: V::Mask, rhs: Rhs) {
1503                *self = mask.select(self.$method(rhs), src);
1504            }
1505
1506            #[inline(always)]
1507            fn [<$method _assign_z>](&mut self, mask: V::Mask, rhs: Rhs) {
1508                *self = mask.select(self.$method(rhs), Self::EMPTY);
1509            }
1510        }
1511    }};
1512}
1513
1514impl_masked!(MUL_ADD: mul_add, mul_sub, nmul_add, nmul_sub, mul_adde, mul_sube, nmul_adde, nmul_sube);
1515impl_masked!(Add::add);
1516impl_masked!(Sub::sub);
1517impl_masked!(Mul::mul);
1518impl_masked!(Div::div);
1519impl_masked!(Rem::rem);
1520
1521// =====================================================================================
1522// Lane-alternating add/sub (`AddSubExt`). Double-double add/sub mix `value`/`error`
1523// via two_sum/two_diff, so `addsub` can NOT be done component-wise. But a *sign
1524// flip* is component-wise-exact, so `neg_even` (flip even-lane signs of both
1525// components) is, and then a single real double-double add finishes the job:
1526//   addsub(a, b)      = a + neg_even(b)
1527//   fmaddsub(a, b, c) = a*b + neg_even(c)   (via the double-double fused mul_adde)
1528//   fmsubadd(a, b, c) = a*b - neg_even(c)
1529// =====================================================================================
1530
1531#[inline(always)]
1532fn neg_even_compensated<V: CompensatedFloatVector>(x: Compensated<V>) -> Compensated<V> {
1533    // `addsub(0, w) = [-w0, w1, -w2, ...]` flips the even lanes exactly.
1534    Compensated {
1535        value: V::ZERO.addsub(x.value),
1536        error: V::ZERO.addsub(x.error),
1537    }
1538}
1539
1540impl<V: CompensatedFloatVector> AddSubExt for Compensated<V> {
1541    type Output = Self;
1542
1543    #[inline(always)]
1544    fn addsub(self, b: Self) -> Self {
1545        self + neg_even_compensated(b)
1546    }
1547    #[inline(always)]
1548    fn fmaddsub(self, b: Self, c: Self) -> Self {
1549        self.mul_adde(b, neg_even_compensated(c))
1550    }
1551    #[inline(always)]
1552    fn fmsubadd(self, b: Self, c: Self) -> Self {
1553        self.mul_sube(b, neg_even_compensated(c))
1554    }
1555}
1556
1557impl<V: CompensatedFloatVector> AddSubExtMasked<V::Mask> for Compensated<V> {
1558    #[inline(always)]
1559    fn addsub_c(self, mask: V::Mask, b: Self) -> Self {
1560        mask.select(self.addsub(b), self)
1561    }
1562    #[inline(always)]
1563    fn addsub_m(self, src: Self, mask: V::Mask, b: Self) -> Self {
1564        mask.select(self.addsub(b), src)
1565    }
1566    #[inline(always)]
1567    fn addsub_z(self, mask: V::Mask, b: Self) -> Self {
1568        mask.select(self.addsub(b), Self::EMPTY)
1569    }
1570
1571    #[inline(always)]
1572    fn fmaddsub_c(self, mask: V::Mask, b: Self, c: Self) -> Self {
1573        mask.select(self.fmaddsub(b, c), self)
1574    }
1575    #[inline(always)]
1576    fn fmaddsub_m(self, src: Self, mask: V::Mask, b: Self, c: Self) -> Self {
1577        mask.select(self.fmaddsub(b, c), src)
1578    }
1579    #[inline(always)]
1580    fn fmaddsub_z(self, mask: V::Mask, b: Self, c: Self) -> Self {
1581        mask.select(self.fmaddsub(b, c), Self::EMPTY)
1582    }
1583
1584    #[inline(always)]
1585    fn fmsubadd_c(self, mask: V::Mask, b: Self, c: Self) -> Self {
1586        mask.select(self.fmsubadd(b, c), self)
1587    }
1588    #[inline(always)]
1589    fn fmsubadd_m(self, src: Self, mask: V::Mask, b: Self, c: Self) -> Self {
1590        mask.select(self.fmsubadd(b, c), src)
1591    }
1592    #[inline(always)]
1593    fn fmsubadd_z(self, mask: V::Mask, b: Self, c: Self) -> Self {
1594        mask.select(self.fmsubadd(b, c), Self::EMPTY)
1595    }
1596}
1597
1598// `_c`/`_m`/`_z` masked variants of the inherent unary (`fn m(self) -> Self`) and
1599// binary (`fn m(self, Self) -> Self`) vector ops, as plain select blends, the
1600// same pattern `impl_masked!` uses for the `core::ops` methods above. Invoked
1601// inside the relevant trait impls below.
1602macro_rules! compensated_masked {
1603    (unary: $($m:ident),* $(,)?) => { paste::paste! {
1604        $(
1605            #[inline(always)] fn [<$m _c>](self, mask: Self::Mask) -> Self { mask.select(self.$m(), self) }
1606            #[inline(always)] fn [<$m _m>](self, src: Self, mask: Self::Mask) -> Self { mask.select(self.$m(), src) }
1607            #[inline(always)] fn [<$m _z>](self, mask: Self::Mask) -> Self { mask.select(self.$m(), Self::EMPTY) }
1608        )*
1609    }};
1610    (binary: $($m:ident),* $(,)?) => { paste::paste! {
1611        $(
1612            #[inline(always)] fn [<$m _c>](self, mask: Self::Mask, rhs: Self) -> Self { mask.select(self.$m(rhs), self) }
1613            #[inline(always)] fn [<$m _m>](self, src: Self, mask: Self::Mask, rhs: Self) -> Self { mask.select(self.$m(rhs), src) }
1614            #[inline(always)] fn [<$m _z>](self, mask: Self::Mask, rhs: Self) -> Self { mask.select(self.$m(rhs), Self::EMPTY) }
1615        )*
1616    }};
1617}
1618
1619impl<V: CompensatedFloatVector> GenericSelectable for Compensated<V> {
1620    type SelectableMask = <V as GenericSelectable>::SelectableMask;
1621
1622    #[inline(always)]
1623    fn select<M>(mask: M, t: Self, f: Self) -> Self
1624    where
1625        Self::SelectableMask: CastMask<M>,
1626    {
1627        let mask = <Self::SelectableMask as CastMask<M>>::mask_from(mask);
1628
1629        Self {
1630            value: mask.select(t.value, f.value),
1631            error: mask.select(t.error, f.error),
1632        }
1633    }
1634}
1635
1636impl<V: thermite::simd::HasIsa> thermite::simd::HasIsa for Compensated<V> {
1637    type Native = V::Native;
1638
1639    const ISA: thermite::isa::InstructionSet = V::ISA;
1640}
1641
1642impl<V: CompensatedFloatVector> SplatVector<Compensated<V::Element>> for Compensated<V> {
1643    type Splat<T: SplatConst<Compensated<V::Element>>> = Self;
1644}
1645
1646#[rustfmt::skip]
1647impl<V: CompensatedFloatVector, E: SplatConst<Compensated<V::Element>>> VectorValue<E, Compensated<V>> for Compensated<V> {
1648    const VALUE: Compensated<V> = const {
1649        struct Value<V: CompensatedFloatVector, E: SplatConst<Compensated<V::Element>>>(core::marker::PhantomData<(V, E)>);
1650        struct Error<V: CompensatedFloatVector, E: SplatConst<Compensated<V::Element>>>(core::marker::PhantomData<(V, E)>);
1651
1652        impl<V: CompensatedFloatVector, E: SplatConst<Compensated<V::Element>>> SplatConst<V::Element> for Value<V, E> {
1653            const VALUE: V::Element = <E as SplatConst<Compensated<V::Element>>>::VALUE.value;
1654        }
1655
1656        impl<V: CompensatedFloatVector, E: SplatConst<Compensated<V::Element>>> SplatConst<V::Element> for Error<V, E> {
1657            const VALUE: V::Element = <E as SplatConst<Compensated<V::Element>>>::VALUE.error;
1658        }
1659
1660        Compensated {
1661            value: thermite::vector::const_splat::<V, Value<V, E>>(),
1662            error: thermite::vector::const_splat::<V, Error<V, E>>(),
1663        }
1664    };
1665}
1666
1667#[rustfmt::skip]
1668/// The lane-sort key: strictly-before under the lexicographic (value, error)
1669/// order, i.e. `cmp_lt`. See `thermite::sort::SortKey` for why this is a
1670/// static trait method and not a closure.
1671impl<V: CompensatedFloatVector> thermite::sort::SortKey<Self> for Compensated<V> {
1672    #[inline(always)]
1673    fn key_lt(a: Self, b: Self) -> V::Mask {
1674        a.cmp_lt(b)
1675    }
1676}
1677
1678/// Scalar insertion walk over whole lanes, for widths past the network ladder.
1679/// Quadratic, like core's `sort_any`; compares composite elements through
1680/// `PartialOrd` (lexicographic, matching the vector comparisons).
1681#[inline(always)]
1682fn sort_lanes_scalar<V: NumericVector, O: thermite::sort::SortOrder>(v: V) -> V
1683where
1684    V::Element: PartialOrd,
1685{
1686    let mut out = v;
1687    let mut i = 1;
1688    while i < V::LANES {
1689        let key = out.extractv(i);
1690        let mut j = i;
1691        while j > 0 {
1692            let prev = out.extractv(j - 1);
1693            let misplaced = if O::IS_ASCENDING { prev > key } else { prev < key };
1694            if !misplaced {
1695                break;
1696            }
1697            out = out.insertv(j, prev);
1698            j -= 1;
1699        }
1700        out = out.insertv(j, key);
1701        i += 1;
1702    }
1703    out
1704}
1705
1706impl<V: CompensatedFloatVector> Interleave for Compensated<V> {
1707    #[inline(always)]
1708    fn interleave(self, other: Self) -> (Self, Self) {
1709        let (value_lo, value_hi) = self.value.interleave(other.value);
1710        let (error_lo, error_hi) = self.error.interleave(other.error);
1711
1712        (
1713            Self {
1714                value: value_lo,
1715                error: error_lo,
1716            },
1717            Self {
1718                value: value_hi,
1719                error: error_hi,
1720            },
1721        )
1722    }
1723
1724    #[inline(always)]
1725    fn deinterleave(self, other: Self) -> (Self, Self) {
1726        let (value_lo, value_hi) = self.value.deinterleave(other.value);
1727        let (error_lo, error_hi) = self.error.deinterleave(other.error);
1728
1729        (
1730            Self {
1731                value: value_lo,
1732                error: error_lo,
1733            },
1734            Self {
1735                value: value_hi,
1736                error: error_hi,
1737            },
1738        )
1739    }
1740}
1741
1742#[rustfmt::skip]
1743impl<V: CompensatedFloatVector> GenericVector for Compensated<V> {
1744    /// Forwarded, not inherited: the empty default would drop the marker, and a
1745    /// kernel traced through this type would lose its loop structure.
1746    #[inline(always)]
1747    #[track_caller]
1748    fn _loop_hint() {
1749        V::_loop_hint()
1750    }
1751
1752    #[inline(always)]
1753    #[track_caller]
1754    fn _enter(name: &'static str) -> u32 {
1755        V::_enter_tagged("Compensated", name)
1756    }
1757
1758    #[inline(always)]
1759    #[track_caller]
1760    fn _enter_tagged(tag: &'static str, name: &'static str) -> u32 {
1761        V::_enter_tagged(tag, name)
1762    }
1763
1764    #[inline(always)]
1765    #[track_caller]
1766    fn _exit(token: u32) {
1767        V::_exit(token)
1768    }
1769
1770    #[inline(always)]
1771    #[track_caller]
1772    fn _region_arg(mut self, token: u32) -> Self {
1773        self.value = self.value._region_arg(token);
1774        self.error = self.error._region_arg(token);
1775        self
1776    }
1777
1778    #[inline(always)]
1779    #[track_caller]
1780    fn _region_result(mut self, token: u32) -> Self {
1781        self.value = self.value._region_result(token);
1782        self.error = self.error._region_result(token);
1783        self
1784    }
1785
1786    #[inline(always)]
1787    #[track_caller]
1788    fn _region_imm(token: u32, imm: core::fmt::Arguments) {
1789        V::_region_imm(token, imm)
1790    }
1791
1792    type Element = Compensated<V::Element>;
1793
1794    const EMPTY: Self = Self::new(V::ZERO);
1795    const LANES: usize = V::LANES;
1796
1797    type Lanes = V::Lanes;
1798
1799    type Unsigned = V::Unsigned;
1800    type Signed = V::Signed;
1801
1802    type Mask = V::Mask;
1803
1804    // value and error move through the same permutation, so a permuted
1805    // compensated number stays a valid (value, error) pair.
1806    #[inline(always)]
1807    fn permutev(self, indices: Self::Unsigned) -> Self {
1808        Self {
1809            value: self.value.permutev(indices),
1810            error: self.error.permutev(indices),
1811        }
1812    }
1813
1814    #[inline(always)]
1815    fn swizzle(self, other: Self, indices: Self::Unsigned) -> Self {
1816        Self {
1817            value: self.value.swizzle(other.value, indices),
1818            error: self.error.swizzle(other.error, indices),
1819        }
1820    }
1821
1822    #[inline(always)]
1823    fn new<const N: usize>(value: [Self::Element; N]) -> Self
1824    where
1825        thermite::generic_array::typenum::Const<N>: thermite::generic_array::IntoArrayLength<ArrayLength = Self::Lanes>
1826    {
1827        Compensated {
1828            value: V::new(value.map(|c| c.value)),
1829            error: V::new(value.map(|c| c.error)),
1830        }
1831    }
1832
1833    #[inline(always)]
1834    fn into_array(self) -> GenericArray<Self::Element, Self::Lanes> {
1835        let mut arr = GenericArray::default();
1836
1837        for i in 0..Self::LANES {
1838            arr[i] = Compensated {
1839                value: self.value.extractv(i),
1840                error: self.error.extractv(i),
1841            };
1842        }
1843
1844        arr
1845    }
1846
1847    #[inline(always)]
1848    fn splat(value: Self::Element) -> Self {
1849        Self {
1850            value: V::splat(value.value),
1851            error: V::splat(value.error),
1852        }
1853    }
1854
1855    #[inline(always)]
1856    fn single(value: Self::Element) -> Self {
1857        Self::new(V::single(value.value))
1858    }
1859
1860    #[inline(always)]
1861    unsafe fn load(ptr: *const Self::Element) -> Self {
1862        let ptr = ptr as *const V::Element;
1863        let a = unsafe { V::load(ptr) };
1864        let b = unsafe { V::load(ptr.add(V::LANES)) };
1865        let (value, error) = a.deinterleave(b);
1866        Self { value, error }
1867    }
1868
1869    #[inline(always)]
1870    fn interleave_by<const GROUP: usize>(self, other: Self) -> (Self, Self) {
1871        let (value_lo, value_hi) = self.value.interleave_by::<GROUP>(other.value);
1872        let (error_lo, error_hi) = self.error.interleave_by::<GROUP>(other.error);
1873        (Self { value: value_lo, error: error_lo }, Self { value: value_hi, error: error_hi })
1874    }
1875
1876    #[inline(always)]
1877    fn deinterleave_by<const GROUP: usize>(self, other: Self) -> (Self, Self) {
1878        let (value_lo, value_hi) = self.value.deinterleave_by::<GROUP>(other.value);
1879        let (error_lo, error_hi) = self.error.deinterleave_by::<GROUP>(other.error);
1880        (Self { value: value_lo, error: error_lo }, Self { value: value_hi, error: error_hi })
1881    }
1882
1883    #[inline(always)]
1884    fn interleave_radix<const N: usize>(inputs: [Self; N]) -> [Self; N] {
1885        let (mut value, mut error) = ([V::EMPTY; N], [V::EMPTY; N]);
1886        for i in 0..N {
1887            value[i] = inputs[i].value;
1888            error[i] = inputs[i].error;
1889        }
1890        let value = V::interleave_radix::<N>(value);
1891        let error = V::interleave_radix::<N>(error);
1892        let mut out = [Self::EMPTY; N];
1893        for i in 0..N {
1894            out[i] = Self { value: value[i], error: error[i] };
1895        }
1896        out
1897    }
1898
1899    #[inline(always)]
1900    fn deinterleave_radix<const N: usize>(inputs: [Self; N]) -> [Self; N] {
1901        let (mut value, mut error) = ([V::EMPTY; N], [V::EMPTY; N]);
1902        for i in 0..N {
1903            value[i] = inputs[i].value;
1904            error[i] = inputs[i].error;
1905        }
1906        let value = V::deinterleave_radix::<N>(value);
1907        let error = V::deinterleave_radix::<N>(error);
1908        let mut out = [Self::EMPTY; N];
1909        for i in 0..N {
1910            out[i] = Self { value: value[i], error: error[i] };
1911        }
1912        out
1913    }
1914
1915    #[inline(always)]
1916    fn deinterleave_radix_by<const N: usize, const GROUP: usize>(inputs: [Self; N]) -> [Self; N] {
1917        let (mut value, mut error) = ([V::EMPTY; N], [V::EMPTY; N]);
1918        for i in 0..N {
1919            value[i] = inputs[i].value;
1920            error[i] = inputs[i].error;
1921        }
1922        let value = V::deinterleave_radix_by::<N, GROUP>(value);
1923        let error = V::deinterleave_radix_by::<N, GROUP>(error);
1924        let mut out = [Self::EMPTY; N];
1925        for i in 0..N {
1926            out[i] = Self { value: value[i], error: error[i] };
1927        }
1928        out
1929    }
1930
1931    #[inline(always)]
1932    fn interleave_radix_by<const N: usize, const GROUP: usize>(inputs: [Self; N]) -> [Self; N] {
1933        let (mut value, mut error) = ([V::EMPTY; N], [V::EMPTY; N]);
1934        for i in 0..N {
1935            value[i] = inputs[i].value;
1936            error[i] = inputs[i].error;
1937        }
1938        let value = V::interleave_radix_by::<N, GROUP>(value);
1939        let error = V::interleave_radix_by::<N, GROUP>(error);
1940        let mut out = [Self::EMPTY; N];
1941        for i in 0..N {
1942            out[i] = Self { value: value[i], error: error[i] };
1943        }
1944        out
1945    }
1946
1947    /// A `Compensated` element is `#[repr(C)]` over two floats (value, error),
1948    /// so `M` interleaved `Compensated` streams are exactly `2 * M`
1949    /// interleaved float streams, precisely a grouped problem with `TAIL = 1`
1950    /// (see [`StreamGroup`]). This hands `M` straight to the inner vector's
1951    /// [`GenericVector::load_deinterleaved_grouped`] (a NEON `LD2`/`LD3`/`LD4`,
1952    /// or a shuffle network on x86), for any `M`: no dispatch ladder, no
1953    /// scalar fallback.
1954    #[inline(always)]
1955    unsafe fn load_deinterleaved<const M: usize>(ptr: *const Self::Element) -> [Self; M] {
1956        let groups = unsafe { V::load_deinterleaved_grouped::<M, 1>(ptr as *const V::Element) };
1957
1958        let mut out = [<Compensated<V> as GenericVector>::EMPTY; M];
1959        let mut j = 0;
1960        while j < M {
1961            out[j] = Compensated { value: groups[j].head, error: groups[j].tail[0] };
1962            j += 1;
1963        }
1964        out
1965    }
1966
1967    /// The exact inverse of [`load_deinterleaved`](Self::load_deinterleaved).
1968    #[inline(always)]
1969    unsafe fn store_interleaved<const M: usize>(ptr: *mut Self::Element, values: [Self; M]) {
1970        let mut groups = [StreamGroup { head: V::ZERO, tail: [V::ZERO; 1] }; M];
1971        let mut j = 0;
1972        while j < M {
1973            groups[j] = StreamGroup { head: values[j].value, tail: [values[j].error] };
1974            j += 1;
1975        }
1976        unsafe { V::store_interleaved_grouped::<M, 1>(ptr as *mut V::Element, groups) }
1977    }
1978
1979    #[inline(always)]
1980    unsafe fn load_m(src: Self, mask: Self::Mask, ptr: *const Self::Element) -> Self {
1981        let ptr = ptr as *const V::Element;
1982        // Expand mask to cover the interleaved (value, error) pairs in memory:
1983        // lane i of mask -> positions 2i and 2i+1 in the interleaved layout.
1984        let (a_mask, b_mask) = mask.interleave(mask);
1985        let (src_a, src_b) = src.value.interleave(src.error);
1986        let a = unsafe { V::load_m(src_a, a_mask, ptr) };
1987        let b = unsafe { V::load_m(src_b, b_mask, ptr.add(V::LANES)) };
1988        let (value, error) = a.deinterleave(b);
1989        Self { value, error }
1990    }
1991
1992    #[inline(always)]
1993    unsafe fn load_z(mask: Self::Mask, ptr: *const Self::Element) -> Self {
1994        let ptr = ptr as *const V::Element;
1995        // Expand mask to cover the interleaved (value, error) pairs in memory.
1996        let (a_mask, b_mask) = mask.interleave(mask);
1997        let a = unsafe { V::load_z(a_mask, ptr) };
1998        let b = unsafe { V::load_z(b_mask, ptr.add(V::LANES)) };
1999        let (value, error) = a.deinterleave(b);
2000        Self { value, error }
2001    }
2002
2003    #[inline(always)]
2004    unsafe fn load_unaligned(ptr: *const Self::Element) -> Self {
2005        let ptr = ptr as *const V::Element;
2006        let a = unsafe { V::load_unaligned(ptr) };
2007        let b = unsafe { V::load_unaligned(ptr.add(V::LANES)) };
2008        let (value, error) = a.deinterleave(b);
2009        Self { value, error }
2010    }
2011
2012    #[inline(always)]
2013    unsafe fn load_streaming(ptr: *const Self::Element) -> Self {
2014        let ptr = ptr as *const V::Element;
2015        let a = unsafe { V::load_streaming(ptr) };
2016        let b = unsafe { V::load_streaming(ptr.add(V::LANES)) };
2017        let (value, error) = a.deinterleave(b);
2018        Self { value, error }
2019    }
2020
2021    #[inline(always)]
2022    unsafe fn store(self, ptr: *mut Self::Element) {
2023        let ptr = ptr as *mut V::Element;
2024        let (a, b) = self.value.interleave(self.error);
2025        unsafe {
2026            a.store(ptr);
2027            b.store(ptr.add(V::LANES));
2028        }
2029    }
2030
2031    #[inline(always)]
2032    unsafe fn store_masked(self, mask: Self::Mask, ptr: *mut Self::Element) {
2033        let ptr = ptr as *mut V::Element;
2034        // Expand mask to cover the interleaved (value, error) pairs in memory.
2035        let (a_mask, b_mask) = mask.interleave(mask);
2036        let (a, b) = self.value.interleave(self.error);
2037        unsafe {
2038            a.store_masked(a_mask, ptr);
2039            b.store_masked(b_mask, ptr.add(V::LANES));
2040        }
2041    }
2042
2043    #[inline(always)]
2044    unsafe fn store_unaligned(self, ptr: *mut Self::Element) {
2045        let ptr = ptr as *mut V::Element;
2046        let (a, b) = self.value.interleave(self.error);
2047        unsafe {
2048            a.store_unaligned(ptr);
2049            b.store_unaligned(ptr.add(V::LANES));
2050        }
2051    }
2052
2053    #[inline(always)]
2054    unsafe fn store_streaming(self, ptr: *mut Self::Element) {
2055        let ptr = ptr as *mut V::Element;
2056        let (a, b) = self.value.interleave(self.error);
2057        unsafe {
2058            a.store_streaming(ptr);
2059            b.store_streaming(ptr.add(V::LANES));
2060        }
2061    }
2062
2063    #[inline(always)]
2064    unsafe fn lookup_unchecked(values: &[Self::Element], indices: Self::Unsigned) -> Self {
2065        if values.len() > Self::LANES * 2 {
2066            // for large lookup tables just fallback to scalar
2067            let mut res = Self::EMPTY;
2068
2069            for i in 0..Self::LANES {
2070                let Ok(idx) = indices.extractv(i).try_into() else {
2071                    panic!("Index out of bounds for usize");
2072                };
2073
2074                res = res.insertv(i, values[idx]);
2075            }
2076
2077            return res;
2078        }
2079
2080        let values = unsafe  {
2081            core::slice::from_raw_parts(values.as_ptr() as *const V::Element, values.len() * 2)
2082        };
2083
2084        let value_idx = indices << 1;
2085        let error_idx = value_idx + Self::Unsigned::ONE;
2086
2087        let value = unsafe { V::lookup_unchecked(values, value_idx) };
2088        let error = unsafe { V::lookup_unchecked(values, error_idx) };
2089
2090        Self { value, error }
2091    }
2092
2093    #[inline(always)]
2094    fn broadcast<const I: usize>(self) -> Self {
2095        Self {
2096            value: V::broadcast::<I>(self.value),
2097            error: V::broadcast::<I>(self.error),
2098        }
2099    }
2100
2101    #[inline(always)]
2102    fn broadcastv(self, idx: usize) -> Self {
2103        Self {
2104            value: V::broadcastv(self.value, idx),
2105            error: V::broadcastv(self.error, idx),
2106        }
2107    }
2108
2109    #[inline(always)]
2110    fn extract<const I: usize>(self) -> Self::Element {
2111        let value = V::extract::<I>(self.value);
2112        let error = V::extract::<I>(self.error);
2113
2114        Compensated { value, error }
2115    }
2116
2117    #[inline(always)]
2118    fn extractv(self, idx: usize) -> Self::Element {
2119        let value = V::extractv(self.value, idx);
2120        let error = V::extractv(self.error, idx);
2121
2122        Compensated { value, error }
2123    }
2124
2125    #[inline(always)]
2126    fn insert<const I: usize>(self, value: Self::Element) -> Self {
2127        let Compensated { value, error } = value;
2128
2129        Self {
2130            value: V::insert::<I>(self.value, value),
2131            error: V::insert::<I>(self.error, error),
2132        }
2133    }
2134
2135    #[inline(always)]
2136    fn insertv(self, idx: usize, value: Self::Element) -> Self {
2137        let Compensated { value, error } = value;
2138
2139        Self {
2140            value: V::insertv(self.value, idx, value),
2141            error: V::insertv(self.error, idx, error),
2142        }
2143    }
2144
2145    #[inline(always)]
2146    fn reverse(self) -> Self {
2147        Self {
2148            value: self.value.reverse(),
2149            error: self.error.reverse(),
2150        }
2151    }
2152
2153    #[inline(always)]
2154    fn swap_bytes(self) -> Self {
2155        Self {
2156            value: self.value.swap_bytes(),
2157            error: self.error.swap_bytes(),
2158        }
2159    }
2160
2161    #[inline(always)]
2162    fn zz(self, mask: Self::Mask) -> Self {
2163        Self {
2164            value: self.value.zz(mask),
2165            error: self.error.zz(mask),
2166        }
2167    }
2168
2169    #[inline(always)]
2170    fn nz(self, mask: Self::Mask) -> Self {
2171        Self {
2172            value: self.value.nz(mask),
2173            error: self.error.nz(mask),
2174        }
2175    }
2176
2177    #[inline(always)]
2178    fn compress(self, mask: Self::Mask) -> Self {
2179        Self {
2180            value: self.value.compress(mask),
2181            error: self.error.compress(mask),
2182        }
2183    }
2184
2185    #[inline(always)]
2186    fn compress_z(self, mask: Self::Mask) -> Self {
2187        Self {
2188            value: self.value.compress_z(mask),
2189            error: self.error.compress_z(mask),
2190        }
2191    }
2192
2193    // Pure lane movement driven by `mask` alone, so both components take the same
2194    // permutation and each value keeps its own error term. `compress_m` included:
2195    // its keep-lanes come from the population count of the shared mask, so they
2196    // land at the same positions in `value` and `error`.
2197    #[inline(always)]
2198    fn compress_m(self, src: Self, mask: Self::Mask) -> Self {
2199        Self {
2200            value: self.value.compress_m(src.value, mask),
2201            error: self.error.compress_m(src.error, mask),
2202        }
2203    }
2204
2205    #[inline(always)]
2206    fn expand(self, mask: Self::Mask) -> Self {
2207        Self {
2208            value: self.value.expand(mask),
2209            error: self.error.expand(mask),
2210        }
2211    }
2212
2213    #[inline(always)]
2214    fn expand_z(self, mask: Self::Mask) -> Self {
2215        Self {
2216            value: self.value.expand_z(mask),
2217            error: self.error.expand_z(mask),
2218        }
2219    }
2220
2221    #[inline(always)]
2222    fn expand_m(self, src: Self, mask: Self::Mask) -> Self {
2223        Self {
2224            value: self.value.expand_m(src.value, mask),
2225            error: self.error.expand_m(src.error, mask),
2226        }
2227    }
2228
2229    #[inline(always)]
2230    fn align<const OFFSET: usize>(self, other: Self) -> Self {
2231        Self {
2232            value: self.value.align::<OFFSET>(other.value),
2233            error: self.error.align::<OFFSET>(other.error),
2234        }
2235    }
2236
2237    // Both components align through `V`, so this is only as native as `V` is.
2238    const HAS_NATIVE_ALIGN: bool = V::HAS_NATIVE_ALIGN;
2239
2240
2241    fn map<F>(mut self, f: F) -> Self
2242    where
2243        F: Fn(Self::Element) -> Self::Element,
2244    {
2245        for i in 0..Self::LANES {
2246            self = self.insertv(i, f(self.extractv(i)));
2247        }
2248
2249        self
2250    }
2251
2252    fn fold<F>(self, mut init: Self::Element, f: F) -> Self::Element
2253    where
2254        F: Fn(Self::Element, Self::Element) -> Self::Element,
2255    {
2256        for i in 0..Self::LANES {
2257            init = f(init, self.extractv(i));
2258        }
2259
2260        init
2261    }
2262
2263    fn reduce<F>(self, f: F) -> Self::Element
2264    where
2265        F: Fn(Self::Element, Self::Element) -> Self::Element,
2266    {
2267        let mut result = self.extractv(0);
2268
2269        for i in 1..Self::LANES {
2270            result = f(result, self.extractv(i));
2271        }
2272
2273        result
2274    }
2275
2276    #[inline(always)] fn splat_m(src: Self, mask: Self::Mask, value: Self::Element) -> Self { mask.select(Self::splat(value), src) }
2277    #[inline(always)] fn splat_z(mask: Self::Mask, value: Self::Element) -> Self { mask.select(Self::splat(value), Self::EMPTY) }
2278    #[inline(always)] fn broadcast_c<const I: usize>(self, mask: Self::Mask) -> Self { mask.select(self.broadcast::<I>(), self) }
2279    #[inline(always)] fn broadcast_m<const I: usize>(self, src: Self, mask: Self::Mask) -> Self { mask.select(self.broadcast::<I>(), src) }
2280    #[inline(always)] fn broadcast_z<const I: usize>(self, mask: Self::Mask) -> Self { mask.select(self.broadcast::<I>(), Self::EMPTY) }
2281    #[inline(always)] fn broadcastv_c(self, mask: Self::Mask, idx: usize) -> Self { mask.select(self.broadcastv(idx), self) }
2282    #[inline(always)] fn broadcastv_m(self, src: Self, mask: Self::Mask, idx: usize) -> Self { mask.select(self.broadcastv(idx), src) }
2283    #[inline(always)] fn broadcastv_z(self, mask: Self::Mask, idx: usize) -> Self { mask.select(self.broadcastv(idx), Self::EMPTY) }
2284    #[inline(always)] fn reverse_c(self, mask: Self::Mask) -> Self { mask.select(self.reverse(), self) }
2285    #[inline(always)] fn reverse_m(self, src: Self, mask: Self::Mask) -> Self { mask.select(self.reverse(), src) }
2286    #[inline(always)] fn reverse_z(self, mask: Self::Mask) -> Self { mask.select(self.reverse(), Self::EMPTY) }
2287    #[inline(always)] fn swap_bytes_c(self, mask: Self::Mask) -> Self { mask.select(self.swap_bytes(), self) }
2288    #[inline(always)] fn swap_bytes_m(self, src: Self, mask: Self::Mask) -> Self { mask.select(self.swap_bytes(), src) }
2289    #[inline(always)] fn swap_bytes_z(self, mask: Self::Mask) -> Self { mask.select(self.swap_bytes(), Self::EMPTY) }
2290}
2291
2292#[rustfmt::skip]
2293impl<V: CompensatedFloatVector> PartialOrdVector for Compensated<V> {
2294    #[inline(always)]
2295    fn cmp_eq(self, other: Self) -> Self::Mask {
2296        // Strictly equal if both components match
2297        self.value.cmp_eq(other.value) & self.error.cmp_eq(other.error)
2298    }
2299
2300    #[inline(always)]
2301    fn cmp_ne(self, other: Self) -> Self::Mask {
2302        // Not equal if either component differs
2303        self.value.cmp_ne(other.value) | self.error.cmp_ne(other.error)
2304    }
2305
2306    #[inline(always)]
2307    fn cmp_lt(self, other: Self) -> Self::Mask {
2308        let val_lt = self.value.cmp_lt(other.value);
2309        let val_eq = self.value.cmp_eq(other.value);
2310        let err_lt = self.error.cmp_lt(other.error);
2311
2312        // (value < other.value) OR (value == other.value AND error < other.error)
2313        GenericMask::ternlog::<{ thermite::ternlog_imm!(A | (B & C)) }>(val_lt, val_eq, err_lt)
2314    }
2315
2316    #[inline(always)]
2317    fn cmp_gt(self, other: Self) -> Self::Mask {
2318        let val_gt = self.value.cmp_gt(other.value);
2319        let val_eq = self.value.cmp_eq(other.value);
2320        let err_gt = self.error.cmp_gt(other.error);
2321
2322        // (value > other.value) OR (value == other.value AND error > other.error)
2323        GenericMask::ternlog::<{ thermite::ternlog_imm!(A | (B & C)) }>(val_gt, val_eq, err_gt)
2324    }
2325
2326    #[inline(always)]
2327    fn cmp_le(self, other: Self) -> Self::Mask {
2328        let val_lt = self.value.cmp_lt(other.value);
2329        let val_eq = self.value.cmp_eq(other.value);
2330        let err_le = self.error.cmp_le(other.error);
2331
2332        // (value < other.value) OR (value == other.value AND error <= other.error)
2333        GenericMask::ternlog::<{ thermite::ternlog_imm!(A | (B & C)) }>(val_lt, val_eq, err_le)
2334    }
2335
2336    #[inline(always)]
2337    fn cmp_ge(self, other: Self) -> Self::Mask {
2338        let val_gt = self.value.cmp_gt(other.value);
2339        let val_eq = self.value.cmp_eq(other.value);
2340        let err_ge = self.error.cmp_ge(other.error);
2341
2342        // (value > other.value) OR (value == other.value AND error >= other.error)
2343        GenericMask::ternlog::<{ thermite::ternlog_imm!(A | (B & C)) }>(val_gt, val_eq, err_ge)
2344    }
2345}
2346
2347impl<V: ScalarValue> core::iter::Sum for Compensated<V> {
2348    #[inline]
2349    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
2350        let mut iter = iter.into_iter();
2351
2352        let Some(mut total) = iter.next() else {
2353            return Compensated::new(V::SCALAR_ZERO);
2354        };
2355
2356        for v in iter {
2357            total += v;
2358        }
2359
2360        total
2361    }
2362}
2363
2364impl<V: ScalarValue> core::iter::Product for Compensated<V> {
2365    #[inline]
2366    fn product<I: Iterator<Item = Self>>(iter: I) -> Self {
2367        let mut iter = iter.into_iter();
2368
2369        let Some(mut total) = iter.next() else {
2370            return Compensated::new(V::SCALAR_ONE); // multiplicative identity
2371        };
2372
2373        for v in iter {
2374            total *= v;
2375        }
2376
2377        total
2378    }
2379}
2380
2381// These are odd in that (value + error) can exceed the bounds of V,
2382// but this is the most sensible implementation.
2383#[rustfmt::skip]
2384impl<V: CompensatedFloatVector> num_traits::Bounded for Compensated<V> {
2385    #[inline(always)] fn min_value() -> Self { Self { value: V::MIN, error: V::MIN } }
2386    #[inline(always)] fn max_value() -> Self { Self { value: V::MAX, error: V::MAX } }
2387}
2388
2389impl<V: CompensatedFloatVector> NumericVector for Compensated<V> {
2390    // The integer conversions are real/value-only in both directions: an integer has no
2391    // derivative, no imaginary part and no error term, so converting one in yields a
2392    // constant, and converting out is the value part alone.
2393    #[inline(always)]
2394    fn to_signed_integer(self) -> Self::Signed {
2395        self.value().to_signed_integer()
2396    }
2397
2398    #[inline(always)]
2399    fn from_signed_integer(v: Self::Signed) -> Self {
2400        Self::new(V::from_signed_integer(v))
2401    }
2402
2403    #[inline(always)]
2404    fn to_unsigned_integer(self) -> Self::Unsigned {
2405        self.value().to_unsigned_integer()
2406    }
2407
2408    #[inline(always)]
2409    fn from_unsigned_integer(v: Self::Unsigned) -> Self {
2410        Self::new(V::from_unsigned_integer(v))
2411    }
2412
2413    const ZERO: Self = Self::new(V::ZERO);
2414    const ONE: Self = Self::new(V::ONE);
2415    const TWO: Self = Self::new(V::TWO);
2416
2417    const MIN: Self = Self {
2418        value: V::MIN,
2419        error: V::MIN,
2420    };
2421
2422    const MAX: Self = Self {
2423        value: V::MAX,
2424        error: V::MAX,
2425    };
2426
2427    /// Lane sorts are keyed on the lexicographic (value, error) order, which
2428    /// is exactly `cmp_lt` here, so the key IS the comparison and ties are
2429    /// deterministic. Each compare-exchange derives one routing mask from it
2430    /// and moves both components through the same permutation and select
2431    /// (`thermite::sort::sort_lanes_by_key`).
2432    #[inline(always)]
2433    fn sort_by<O: thermite::sort::SortOrder>(self) -> Self {
2434        if const { Self::LANES <= 16 && Self::LANES.is_power_of_two() } {
2435            thermite::sort::sort_lanes_by_key::<Self, O, Self>(self)
2436        } else {
2437            sort_lanes_scalar::<Self, O>(self)
2438        }
2439    }
2440
2441    #[inline(always)]
2442    fn bitonic_clean_by<O: thermite::sort::SortOrder>(self) -> Self {
2443        if const { Self::LANES <= 16 && Self::LANES.is_power_of_two() } {
2444            thermite::sort::bitonic_clean_lanes_by_key::<Self, O, Self>(self)
2445        } else {
2446            // A full sort trivially cleans a bitonic input.
2447            sort_lanes_scalar::<Self, O>(self)
2448        }
2449    }
2450
2451    #[inline(always)]
2452    fn is_zero(self) -> Self::Mask {
2453        self.value().is_zero()
2454    }
2455
2456    #[inline(always)]
2457    fn is_all_zero(self) -> bool {
2458        // if value+error is nonzero, then one of the components must be nonzero,
2459        // and this allows for faster vertical reduction versus two calls to is_all_zero()
2460        self.value().is_all_zero()
2461    }
2462
2463    #[inline(always)]
2464    fn min(self, other: Self) -> Self {
2465        self.cmp_lt(other).select(self, other)
2466    }
2467
2468    #[inline(always)]
2469    fn max(self, other: Self) -> Self {
2470        self.cmp_gt(other).select(self, other)
2471    }
2472
2473    #[inline(always)]
2474    fn clamp(self, min: Self, max: Self) -> Self {
2475        let x = self.value();
2476        let min_value = min.value();
2477        let max_value = max.value();
2478
2479        let is_lt = x.cmp_lt(min_value);
2480        let is_gt = x.cmp_gt(max_value);
2481
2482        let value = is_lt.select(min.value, is_gt.select(max.value, self.value));
2483        let error = is_lt.select(min.error, is_gt.select(max.error, self.error));
2484
2485        Self { value, error }
2486    }
2487
2488    #[inline(always)]
2489    fn min_element(self) -> Self::Element {
2490        let mut min_elem = self.extractv(0);
2491        let mut min_value = min_elem.value();
2492
2493        for i in 1..Self::LANES {
2494            let elem = self.extractv(i);
2495            let value = elem.value();
2496
2497            if value < min_value {
2498                min_elem = elem;
2499                min_value = value;
2500            }
2501        }
2502
2503        min_elem
2504    }
2505
2506    #[inline(always)]
2507    fn max_element(self) -> Self::Element {
2508        let mut max_elem = self.extractv(0);
2509        let mut max_value = max_elem.value();
2510
2511        for i in 1..Self::LANES {
2512            let elem = self.extractv(i);
2513            let value = elem.value();
2514
2515            if value > max_value {
2516                max_elem = elem;
2517                max_value = value;
2518            }
2519        }
2520
2521        max_elem
2522    }
2523
2524    fn sum_elements(self) -> Self::Element {
2525        self.reduce(|a, b| a + b)
2526    }
2527
2528    fn prod_elements(self) -> Self::Element {
2529        self.reduce(|a, b| a * b)
2530    }
2531
2532    // None of these are componentwise. Scanning `value` and `error` with the inner
2533    // vector's own scan would add the value lanes without ever renormalising the
2534    // carried error into them, which is exactly the compensation this type exists to
2535    // do, so the ladder runs on `Self`'s double-double `+`, like `sum_elements`
2536    // reduces through `+` rather than through the components.
2537    //
2538    // The ladder associates the additions as a tree where a sequential scan would
2539    // chain them, so the result is not bit-identical to folding lane by lane. Same
2540    // reassociation the core `prefix_sum` documents, at double-double precision.
2541    #[inline(always)]
2542    fn prefix_sum(self) -> Self {
2543        thermite::scan_ladder!(forward, self, Self::ZERO, core::ops::Add::add)
2544    }
2545
2546    #[inline(always)]
2547    fn reverse_prefix_sum(self) -> Self {
2548        thermite::scan_ladder!(reverse, self, Self::ZERO, core::ops::Add::add)
2549    }
2550
2551    // min/max order by the compensated value and carry the winning lane's error with
2552    // it, so they scan whole elements through `Self::min`/`Self::max` above.
2553    #[inline(always)]
2554    fn prefix_min(self) -> Self {
2555        thermite::scan_ladder!(forward, self, self.broadcast::<0>(), Self::min)
2556    }
2557
2558    #[inline(always)]
2559    fn prefix_max(self) -> Self {
2560        thermite::scan_ladder!(forward, self, self.broadcast::<0>(), Self::max)
2561    }
2562
2563    #[inline(always)]
2564    fn reverse_prefix_min(self) -> Self {
2565        thermite::scan_ladder!(reverse, self, Self::splat(self.last_element()), Self::min)
2566    }
2567
2568    #[inline(always)]
2569    fn reverse_prefix_max(self) -> Self {
2570        thermite::scan_ladder!(reverse, self, Self::splat(self.last_element()), Self::max)
2571    }
2572
2573    #[inline(always)]
2574    fn offset() -> Self {
2575        Self::new(V::offset())
2576    }
2577
2578    #[inline(always)]
2579    fn indexed() -> Self {
2580        Self::new(V::indexed())
2581    }
2582
2583    compensated_masked!(binary: min, max);
2584
2585    // Semantically `self * Self::splat(factor)`; the error term participates in the
2586    // double-double product, so there is no cheaper compensated form to specialize.
2587    #[inline(always)]
2588    fn scale(self, factor: Self::Element) -> Self {
2589        self * Self::splat(factor)
2590    }
2591
2592    #[inline(always)]
2593    fn scale_c(self, mask: Self::Mask, factor: Self::Element) -> Self {
2594        mask.select(self.scale(factor), self)
2595    }
2596
2597    #[inline(always)]
2598    fn scale_m(self, src: Self, mask: Self::Mask, factor: Self::Element) -> Self {
2599        mask.select(self.scale(factor), src)
2600    }
2601
2602    #[inline(always)]
2603    fn scale_z(self, mask: Self::Mask, factor: Self::Element) -> Self {
2604        mask.select(self.scale(factor), Self::EMPTY)
2605    }
2606
2607    // Deinterleaving `(lo, hi)` yields the even- and odd-indexed elements already in
2608    // pairwise order (`[lo0, lo2, hi0, hi2...]` / `[lo1, lo3, hi1, hi3...]`), so the
2609    // pair sums reduce to a single compensated (two-sum) add.
2610    #[inline(always)]
2611    fn pairwise_sum(lo: Self, hi: Self) -> Self {
2612        let (even, odd) = lo.deinterleave(hi);
2613        even + odd
2614    }
2615
2616    // The compensated add dominates the cost and the shuffle has no cheaper
2617    // relaxed form, so the strictly-ordered result is returned as-is.
2618    #[inline(always)]
2619    fn relaxed_pairwise_sum(lo: Self, hi: Self) -> Self {
2620        Self::pairwise_sum(lo, hi)
2621    }
2622
2623    fn min_max_element(self) -> (Self::Element, Self::Element) {
2624        (self.min_element(), self.max_element())
2625    }
2626
2627    // Ordering is by the folded value+error, consistent with min/max_element.
2628    #[inline(always)]
2629    fn arg_minmax(self) -> (usize, usize) {
2630        self.value().arg_minmax()
2631    }
2632}
2633
2634impl<V: CompensatedFloatVector> thermite::vector::ops::NegMasked<V::Mask> for Compensated<V> {
2635    #[inline(always)]
2636    fn neg_c(mut self, mask: V::Mask) -> Self {
2637        self.value = self.value.neg_c(mask);
2638        self.error = self.error.neg_c(mask);
2639
2640        self
2641    }
2642
2643    #[inline(always)]
2644    fn neg_m(mut self, src: Self, mask: V::Mask) -> Self {
2645        self.value = self.value.neg_m(src.value, mask);
2646        self.error = self.error.neg_m(src.error, mask);
2647
2648        self
2649    }
2650
2651    #[inline(always)]
2652    fn neg_z(mut self, mask: V::Mask) -> Self {
2653        self.value = self.value.neg_z(mask);
2654        self.error = self.error.neg_z(mask);
2655
2656        self
2657    }
2658}
2659
2660impl<V: CompensatedFloatVector> SignedVector for Compensated<V> {
2661    const NEG_ONE: Self = Self::new(V::NEG_ONE);
2662    const MIN_POSITIVE: Self = Self::new(V::MIN_POSITIVE);
2663
2664    #[inline(always)]
2665    fn abs(self) -> Self {
2666        self.neg_c(self.value().cmp_lt(V::ZERO))
2667    }
2668
2669    #[inline(always)]
2670    fn signum(self) -> Self {
2671        Self::new(self.value().signum())
2672    }
2673
2674    #[inline(always)]
2675    fn is_positive(self) -> Self::Mask {
2676        self.value().is_positive()
2677    }
2678
2679    #[inline(always)]
2680    fn is_negative(self) -> Self::Mask {
2681        self.value().is_negative()
2682    }
2683
2684    #[inline(always)]
2685    fn select_negative(self, if_neg: Self, if_pos: Self) -> Self {
2686        self.is_negative().select(if_neg, if_pos)
2687    }
2688
2689    #[inline(always)]
2690    fn copysign(self, sign: Self) -> Self {
2691        let self_is_neg = self.is_negative();
2692        let sign_is_neg = sign.is_negative();
2693
2694        self.neg_c(self_is_neg ^ sign_is_neg)
2695    }
2696
2697    // `abs` is a negation of the negative lanes, so the conditional form just
2698    // restricts that negation to the masked lanes: one blend instead of
2699    // computing a full `abs` and re-blending it. Same for `copysign`.
2700    #[inline(always)]
2701    fn abs_c(self, mask: Self::Mask) -> Self {
2702        self.neg_c(mask & self.value().cmp_lt(V::ZERO))
2703    }
2704
2705    #[inline(always)]
2706    fn abs_m(self, src: Self, mask: Self::Mask) -> Self {
2707        mask.select(self.abs(), src)
2708    }
2709
2710    #[inline(always)]
2711    fn abs_z(self, mask: Self::Mask) -> Self {
2712        mask.select(self.abs(), Self::EMPTY)
2713    }
2714
2715    #[inline(always)]
2716    fn copysign_c(self, mask: Self::Mask, sign: Self) -> Self {
2717        self.neg_c(mask & (self.is_negative() ^ sign.is_negative()))
2718    }
2719
2720    #[inline(always)]
2721    fn copysign_m(self, src: Self, mask: Self::Mask, sign: Self) -> Self {
2722        mask.select(self.copysign(sign), src)
2723    }
2724
2725    #[inline(always)]
2726    fn copysign_z(self, mask: Self::Mask, sign: Self) -> Self {
2727        mask.select(self.copysign(sign), Self::EMPTY)
2728    }
2729}
2730
2731// We need a single generic form of this since Rust only allows one
2732// implementation of this given the nested generic parameters.
2733impl<FROM, TO> CastVector<Compensated<FROM>> for Compensated<TO>
2734where
2735    FROM: CompensatedFloatVector + CastVector<TO>,
2736    TO: CompensatedFloatVector + CastVector<FROM>,
2737{
2738    fn cast_into(self) -> Compensated<FROM> {
2739        Compensated::<FROM>::cast_from(self)
2740    }
2741
2742    fn cast_from(from: Compensated<FROM>) -> Self {
2743        let from_size = size_of::<FROM::Element>();
2744        let to_size = size_of::<TO::Element>();
2745
2746        // TODO: Maybe match on cmp ordering?
2747        // Ord::cmp(&size_of::<FROM::Element>(), &size_of::<TO::Element>());
2748        if from_size > to_size {
2749            // --- Downsampling (f64-like -> f32-like) ---
2750            // We lose precision, so we must capture the lost bits in the new error term.
2751
2752            // Project High -> Low
2753            let value = TO::cast_from(from.value);
2754
2755            // Project Low -> High (check our work)
2756            // Calculate residual (bits lost in cast) in High Precision
2757            // Strict, twice: this subtraction cancels to exactly the lost bits, and a
2758            // re-bracketing with the join below would drop them.
2759            let (delta, _) = FROM::two_diff(from.value, FROM::cast_from(value));
2760            let (delta, _) = FROM::two_sum(delta, from.error);
2761
2762            Self {
2763                value,
2764                // Accumulate total error (new lost bits + old error)
2765                error: TO::cast_from(delta),
2766            }
2767        } else if from_size < to_size {
2768            // --- Upsampling (f32-like -> f64-like) ---
2769            // The larger type can hold the entire double-double sum losslessly.
2770            // We collapse the pair into the single 'value' field to normalize it.
2771
2772            let v_hi = TO::cast_from(from.value);
2773            let e_hi = TO::cast_from(from.error);
2774
2775            // Since f32+f32 (48 bits effective) fits in f64 (53 bits),
2776            // this sum is exact.
2777            Self {
2778                value: TO::two_sum(v_hi, e_hi).0,
2779                error: TO::ZERO,
2780            }
2781        } else {
2782            // --- Same Precision (f64 -> f64 or f32 -> f32) ---
2783            // Just a type conversion (or SIMD layout change) without precision change.
2784            // Preserve the structure exactly.
2785            Self {
2786                value: TO::cast_from(from.value),
2787                error: TO::cast_from(from.error),
2788            }
2789        }
2790    }
2791}
2792
2793#[rustfmt::skip]
2794impl<V: CompensatedFloatVector> FloatVector for Compensated<V> {
2795    const HALF: Self = Self::new(<V as FloatVector>::HALF);
2796    const NEG_ZERO: Self = Self::new(<V as FloatVector>::NEG_ZERO);
2797    const INFINITY: Self = Self::new(<V as FloatVector>::INFINITY);
2798    const NEG_INFINITY: Self = Self::new(<V as FloatVector>::NEG_INFINITY);
2799    const NAN: Self = Self::new(<V as FloatVector>::NAN);
2800
2801    const EPSILON: Self = Compensated {
2802        value: V::ZERO,
2803        error: <V as FloatVector>::EPSILON, // lower order bits get the epsilon
2804    };
2805
2806    /// Don't use Compensated if you need to go higher precision than it provides.
2807    ///
2808    /// If you absolutely must, use [`CastVector`] to convert to a higher-precision type.
2809    type ExtendedPrecision = Self;
2810
2811    // These are designed to provide reasonable results with reasonable performance.
2812    #[inline(always)] fn is_infinite(self) -> Self::Mask { self.value().is_infinite() }
2813    #[inline(always)] fn is_finite(self) -> Self::Mask { self.value().is_finite() }
2814    #[inline(always)] fn is_nan(self) -> Self::Mask { self.value.is_nan() | self.error.is_nan() }
2815    #[inline(always)] fn is_zero_or_subnormal(self) -> Self::Mask { self.value().is_zero_or_subnormal() }
2816    #[inline(always)] fn is_normal(self) -> Self::Mask { self.value().is_normal() }
2817    #[inline(always)] fn is_subnormal(self) -> Self::Mask { self.value.is_subnormal() | self.error.is_subnormal() }
2818
2819    const HAS_APPROX_RCP: bool = false;
2820    const HAS_APPROX_RSQRT: bool = false;
2821
2822    #[inline(always)]
2823    fn sqrt(self) -> Self {
2824        let s = V::sqrt(self.value);
2825
2826        let (p, e) = ScalarValue::square(s);
2827
2828        // Strict subtractions, see `division_remainder`. `self.value - p` cancels almost
2829        // completely; re-bracketed as `(value + error) - (p + e)` the remainder is
2830        // destroyed. Under `algebraic-scalar` that cost a whole ulp in `sqrt`'s high word.
2831        let (d_value, _) = V::two_diff(self.value, p);
2832        let (d_error, _) = V::two_diff(self.error, e);
2833        let (remainder, _) = V::two_sum(d_value, d_error);
2834
2835        // correction term
2836        let corr = remainder / (s + s);
2837
2838        Self::renormalized(s, corr)
2839    }
2840
2841    #[inline(always)] fn rsqrt(self) -> Self { Self::div_scalar(V::ONE, self.sqrt()) }
2842    #[inline(always)] fn rcp(self) -> Self { Self::div_scalar(V::ONE, self) }
2843
2844    #[inline(always)] fn floor(self) -> Self { Self::new(self.value().floor()) }
2845    #[inline(always)] fn ceil(self) -> Self { Self::new(self.value().ceil()) }
2846    #[inline(always)] fn round(self) -> Self { Self::new(self.value().round()) }
2847    #[inline(always)] fn trunc(self) -> Self { Self::new(self.value().trunc()) }
2848    #[inline(always)] fn fract(self) -> Self { self - self.trunc() }
2849
2850    #[inline(always)]
2851    fn mul_sign(self, sign: Self) -> Self {
2852        let sign = sign.value();
2853
2854        Self {
2855            value: self.value.mul_sign(sign),
2856            error: self.error.mul_sign(sign),
2857        }
2858    }
2859
2860    #[inline(always)] fn signed_zero(self) -> Self { Self::new(self.value().signed_zero()) }
2861
2862    #[inline(always)] fn next_up(self) -> Self { Self::renormalized(self.value, self.error.next_up()) }
2863    #[inline(always)] fn next_down(self) -> Self { Self::renormalized(self.value, self.error.next_down()) }
2864
2865    unsafe fn block_autovectorization(&mut self) {
2866        unsafe {
2867            self.value.block_autovectorization();
2868            self.error.block_autovectorization();
2869        }
2870    }
2871
2872    compensated_masked!(unary: sqrt, rsqrt, rcp, floor, ceil, round, trunc, fract, signed_zero, next_up, next_down);
2873    compensated_masked!(binary: mul_sign);
2874
2875    // mix(t) = a*(1 - t) + b*t = a + (b - a)*t, composed through compensated
2876    // arithmetic (same rearrangement thermite-dual uses).
2877    #[inline(always)]
2878    fn mix(self, a: Self, b: Self) -> Self {
2879        a + (b - a) * self
2880    }
2881}
2882
2883use core::fmt;
2884
2885impl<V: PrettyPrintScalar> fmt::Display for Compensated<V> {
2886    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2887        <V as PrettyPrintScalar>::fmt(self.value, self.error, f)
2888    }
2889}
2890
2891trait PrettyPrintScalar: ScalarValue {
2892    fn fmt(value: Self, error: Self, f: &mut fmt::Formatter<'_>) -> fmt::Result;
2893}
2894
2895impl PrettyPrintScalar for f32 {
2896    fn fmt(value: Self, error: Self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2897        if error == 0.0 {
2898            write!(f, "{value}")
2899        } else {
2900            write!(f, "{}", (value as f64) + (error as f64))
2901        }
2902    }
2903}
2904
2905impl PrettyPrintScalar for f64 {
2906    fn fmt(mut value: Self, mut error: Self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2907        if error == 0.0 {
2908            return write!(f, "{value}");
2909        };
2910
2911        if value < 0.0 {
2912            write!(f, "-")?;
2913
2914            value = -value;
2915            error = -error;
2916        }
2917
2918        let c = Compensated { value, error };
2919
2920        let int_part = Compensated::new(FloatElement::trunc(c.value()));
2921        let mut frac_part = c - int_part;
2922
2923        write!(f, "{}", int_part.value as u64)?;
2924
2925        if frac_part.value() == 0.0 {
2926            return Ok(());
2927        }
2928
2929        f.write_str(".")?;
2930
2931        let p = f.precision().unwrap_or(17); // default to max precision for f64
2932
2933        for _ in 0..p {
2934            frac_part *= 10.0;
2935
2936            let digit = FloatElement::trunc(frac_part.value());
2937
2938            write!(f, "{}", digit as u64)?;
2939
2940            frac_part -= Compensated::new(digit);
2941
2942            if frac_part.value == 0.0 && frac_part.error == 0.0 {
2943                break;
2944            }
2945        }
2946
2947        Ok(())
2948    }
2949}
Last built: 2026-09-08 21:35:55 UTC