Skip to main content

oxiblas_core/scalar/
extended.rs

1//! Extended precision type implementations (f16 and QuadFloat/f128).
2
3#[cfg(any(feature = "f16", feature = "f128"))]
4use super::traits::{Field, Real, Scalar};
5
6// =============================================================================
7// f16 support (half-precision)
8// =============================================================================
9
10#[cfg(feature = "f16")]
11use half::f16;
12
13#[cfg(feature = "f16")]
14impl Scalar for f16 {
15    type Real = f16;
16
17    #[inline]
18    fn abs(self) -> Self::Real {
19        if self < f16::ZERO { -self } else { self }
20    }
21
22    #[inline]
23    fn conj(self) -> Self {
24        self
25    }
26
27    #[inline]
28    fn is_real() -> bool {
29        true
30    }
31
32    #[inline]
33    fn real(self) -> Self::Real {
34        self
35    }
36
37    #[inline]
38    fn imag(self) -> Self::Real {
39        f16::ZERO
40    }
41
42    #[inline]
43    fn from_real_imag(re: Self::Real, _im: Self::Real) -> Self {
44        re
45    }
46
47    #[inline]
48    fn abs_sq(self) -> Self::Real {
49        self * self
50    }
51
52    #[inline]
53    fn epsilon() -> Self::Real {
54        f16::EPSILON
55    }
56
57    #[inline]
58    fn min_positive() -> Self::Real {
59        f16::MIN_POSITIVE
60    }
61
62    #[inline]
63    fn max_value() -> Self::Real {
64        f16::MAX
65    }
66}
67
68#[cfg(feature = "f16")]
69impl Real for f16 {
70    #[inline]
71    fn sqrt(self) -> Self {
72        f16::from_f32(self.to_f32().sqrt())
73    }
74
75    #[inline]
76    fn ln(self) -> Self {
77        f16::from_f32(self.to_f32().ln())
78    }
79
80    #[inline]
81    fn exp(self) -> Self {
82        f16::from_f32(self.to_f32().exp())
83    }
84
85    #[inline]
86    fn sin(self) -> Self {
87        f16::from_f32(self.to_f32().sin())
88    }
89
90    #[inline]
91    fn cos(self) -> Self {
92        f16::from_f32(self.to_f32().cos())
93    }
94
95    #[inline]
96    fn atan2(self, other: Self) -> Self {
97        f16::from_f32(self.to_f32().atan2(other.to_f32()))
98    }
99
100    #[inline]
101    fn powf(self, n: Self) -> Self {
102        f16::from_f32(self.to_f32().powf(n.to_f32()))
103    }
104
105    #[inline]
106    fn signum(self) -> Self {
107        if self > f16::ZERO {
108            f16::ONE
109        } else if self < f16::ZERO {
110            -f16::ONE
111        } else {
112            f16::ZERO
113        }
114    }
115
116    #[inline]
117    fn mul_add(self, a: Self, b: Self) -> Self {
118        f16::from_f32(self.to_f32().mul_add(a.to_f32(), b.to_f32()))
119    }
120
121    #[inline]
122    fn floor(self) -> Self {
123        f16::from_f32(self.to_f32().floor())
124    }
125
126    #[inline]
127    fn ceil(self) -> Self {
128        f16::from_f32(self.to_f32().ceil())
129    }
130
131    #[inline]
132    fn round(self) -> Self {
133        f16::from_f32(self.to_f32().round())
134    }
135
136    #[inline]
137    fn trunc(self) -> Self {
138        f16::from_f32(self.to_f32().trunc())
139    }
140
141    #[inline]
142    fn hypot(self, other: Self) -> Self {
143        f16::from_f32(self.to_f32().hypot(other.to_f32()))
144    }
145}
146
147#[cfg(feature = "f16")]
148impl Field for f16 {
149    #[inline]
150    fn mul_conj(self, other: Self) -> Self {
151        self * other
152    }
153
154    #[inline]
155    fn conj_mul(self, other: Self) -> Self {
156        self * other
157    }
158
159    #[inline]
160    fn recip(self) -> Self {
161        f16::ONE / self
162    }
163
164    #[inline]
165    fn powi(self, n: i32) -> Self {
166        f16::from_f32(self.to_f32().powi(n))
167    }
168}
169
170// =============================================================================
171// QuadFloat (f128 / double-double) support
172// =============================================================================
173
174#[cfg(feature = "f128")]
175use core::fmt::Display;
176#[cfg(feature = "f128")]
177use core::iter::Sum;
178#[cfg(feature = "f128")]
179use core::ops::{
180    Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign,
181};
182#[cfg(feature = "f128")]
183use num_traits::{Float, FromPrimitive, One, Zero};
184#[cfg(feature = "f128")]
185use twofloat::TwoFloat;
186
187/// Quad-precision floating-point type using double-double arithmetic.
188///
189/// This newtype wraps `TwoFloat` from the `twofloat` crate, which provides
190/// approximately 106 bits of mantissa precision (31 decimal digits) using
191/// double-double arithmetic. This gives quadruple precision (similar to IEEE 754
192/// binary128) without requiring platform-specific quadmath libraries.
193///
194/// # Features
195///
196/// - Cross-platform pure Rust implementation
197/// - ~31 decimal digits of precision
198/// - All standard mathematical operations (sin, cos, exp, ln, etc.)
199/// - Compatible with OxiBLAS scalar traits
200///
201/// # Example
202///
203/// ```
204/// # #[cfg(feature = "f128")] {
205/// use num_traits::Float;
206/// use oxiblas_core::scalar::QuadFloat;
207///
208/// let x = QuadFloat::from(2.0);
209/// let y = x.sqrt();
210/// assert!((y * y - x).abs() < QuadFloat::from(1e-30));
211/// # }
212/// ```
213#[cfg(feature = "f128")]
214#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default)]
215#[repr(transparent)]
216pub struct QuadFloat(TwoFloat);
217
218#[cfg(feature = "f128")]
219impl QuadFloat {
220    /// Create a new QuadFloat from a f64
221    #[inline]
222    pub const fn new(value: f64) -> Self {
223        Self(TwoFloat::from_f64(value))
224    }
225
226    /// Get the underlying TwoFloat
227    #[inline]
228    pub const fn inner(self) -> TwoFloat {
229        self.0
230    }
231}
232
233// -----------------------------------------------------------------------------
234// Double-double-aware integer rounding and helpers
235// -----------------------------------------------------------------------------
236//
237// A normalized `TwoFloat` stores a value as two non-overlapping `f64` words
238// `hi + lo`, where `hi` is the correctly rounded `f64` nearest the true value
239// and `lo` is the exact rounding error, so `|lo| <= 0.5 * ulp(hi)`. Rounding the
240// value to an integer therefore cannot be done on `hi` alone: when `hi` is
241// itself an integer the entire fractional part lives in `lo`, and when `hi` is a
242// half-integer the sign of `lo` decides how a tie falls. Each helper below
243// derives the case analysis from that invariant and renormalizes the resulting
244// pair with an error-free `two_sum` (`TwoFloat::new_add`).
245#[cfg(feature = "f128")]
246impl QuadFloat {
247    /// Largest integer `<= self`, at double-double precision (`f64::floor`).
248    ///
249    /// WHY the split: if `hi` is not an integer, the true value stays on the
250    /// same side of every integer as `hi` — the low word is at most half a ULP,
251    /// and had it been able to cross an integer boundary `hi` would already have
252    /// rounded to that integer — so `floor(hi+lo) == floor(hi)`. If `hi` is an
253    /// integer, `floor(hi+lo) = hi + floor(lo)`, which we renormalize.
254    #[inline]
255    fn dd_floor(self) -> Self {
256        let hi = self.0.hi();
257        let floored_hi = hi.floor();
258        if floored_hi != hi {
259            QuadFloat(TwoFloat::from_f64(floored_hi))
260        } else {
261            QuadFloat(TwoFloat::new_add(hi, self.0.lo().floor()))
262        }
263    }
264
265    /// Smallest integer `>= self`, at double-double precision (`f64::ceil`).
266    ///
267    /// Mirror image of [`dd_floor`](Self::dd_floor).
268    #[inline]
269    fn dd_ceil(self) -> Self {
270        let hi = self.0.hi();
271        let ceiled_hi = hi.ceil();
272        if ceiled_hi != hi {
273            QuadFloat(TwoFloat::from_f64(ceiled_hi))
274        } else {
275            QuadFloat(TwoFloat::new_add(hi, self.0.lo().ceil()))
276        }
277    }
278
279    /// Truncation toward zero, at double-double precision (`f64::trunc`).
280    ///
281    /// Truncation is floor for non-negative values and ceil for negative ones;
282    /// the sign of a normalized double-double equals the sign of its high word.
283    #[inline]
284    fn dd_trunc(self) -> Self {
285        if self.0.is_sign_negative() {
286            self.dd_ceil()
287        } else {
288            self.dd_floor()
289        }
290    }
291
292    /// Round half away from zero, at double-double precision (`f64::round`).
293    ///
294    /// WHY the tie handling: a genuine tie (value exactly `k + 0.5`) can arise
295    /// either from `hi` being a half-integer with `lo == 0`, or from `hi` being
296    /// an integer with `lo` a half-integer. Rounding `hi` (or `lo`) in isolation
297    /// with `f64::round` breaks the tie away from *that word's* zero, which is
298    /// the wrong direction whenever the word's sign differs from the whole
299    /// value's sign; those cases are corrected explicitly.
300    #[inline]
301    fn dd_round(self) -> Self {
302        let hi = self.0.hi();
303        let lo = self.0.lo();
304        let rounded_hi = hi.round();
305        if rounded_hi != hi {
306            // `hi` is not an integer, so `round(hi+lo) == round(hi)` unless `hi`
307            // is exactly a half-integer (its own tie), in which case the low
308            // word decides which side of the half-way point the value lies on.
309            if (hi - rounded_hi).abs() == 0.5 {
310                if hi > 0.0 && lo < 0.0 {
311                    QuadFloat(TwoFloat::from_f64(rounded_hi - 1.0))
312                } else if hi < 0.0 && lo > 0.0 {
313                    QuadFloat(TwoFloat::from_f64(rounded_hi + 1.0))
314                } else {
315                    QuadFloat(TwoFloat::from_f64(rounded_hi))
316                }
317            } else {
318                QuadFloat(TwoFloat::from_f64(rounded_hi))
319            }
320        } else {
321            // `hi` is an integer; the fractional part is entirely in `lo`.
322            let mut rounded_lo = lo.round();
323            // On a tie in `lo` whose away-from-zero direction disagrees with the
324            // whole value's away-from-zero direction (opposite signs), pick the
325            // neighbor lying on the whole value's side, i.e. `trunc(lo)`.
326            if (lo - lo.trunc()).abs() == 0.5 && (lo < 0.0) != (hi < 0.0) {
327                rounded_lo = lo.trunc();
328            }
329            QuadFloat(TwoFloat::new_add(hi, rounded_lo))
330        }
331    }
332
333    /// Fractional part `self - trunc(self)`, at double-double precision.
334    ///
335    /// Matches `f64::fract`: the result carries the sign of `self`. The integer
336    /// part is exact, so the subtraction is a well-conditioned double-double
337    /// operation.
338    #[inline]
339    fn dd_fract(self) -> Self {
340        self - self.dd_trunc()
341    }
342
343    /// Overflow-safe Euclidean length `sqrt(self^2 + other^2)`.
344    ///
345    /// WHY the two paths: the direct form `sqrt(a^2 + b^2)` is correctly rounded
346    /// at full double-double precision but overflows to infinity once the larger
347    /// operand exceeds `sqrt(MAX)` (and loses precision to subnormals when both
348    /// are tiny), even when the true result is finite. When the larger magnitude
349    /// is in the safe window we therefore use the direct form; outside it we fall
350    /// back to the scaled form `max * sqrt(1 + (min/max)^2)`, whose squared term
351    /// is bounded to `[0, 1]` so the only overflow that can occur is a genuine
352    /// one. (twofloat's `TwoFloat / TwoFloat` division is not full precision, so
353    /// the scaled path is slightly less accurate — hence it is used only when
354    /// unavoidable.)
355    #[inline]
356    fn dd_hypot(self, other: Self) -> Self {
357        // `sqrt(f64::MAX / 2)`, with headroom so `a*a + b*b` cannot overflow.
358        const HYPOT_MAX_SAFE: f64 = 2.9e153;
359        // Below this the squares would start degrading into the subnormal range.
360        const HYPOT_MIN_SAFE: f64 = 1e-150;
361        let a = QuadFloat(self.0.abs());
362        let b = QuadFloat(other.0.abs());
363        // IEEE-754 hypot special cases: an infinity dominates (even over a NaN),
364        // then a NaN propagates.
365        if a.0.is_infinite() || b.0.is_infinite() {
366            return QuadFloat(TwoFloat::INFINITY);
367        }
368        if a.0.is_nan() || b.0.is_nan() {
369            return QuadFloat(TwoFloat::NAN);
370        }
371        let (max, min) = if a >= b { (a, b) } else { (b, a) };
372        let max_hi = max.0.hi();
373        if max_hi == 0.0 {
374            return QuadFloat::from(0.0);
375        }
376        if (HYPOT_MIN_SAFE..HYPOT_MAX_SAFE).contains(&max_hi) {
377            let sum = self * self + other * other;
378            QuadFloat(sum.0.sqrt())
379        } else {
380            let ratio = min / max;
381            let radicand = QuadFloat::from(1.0) + ratio * ratio;
382            max * QuadFloat(radicand.0.sqrt())
383        }
384    }
385
386    /// Real cube root, at double-double precision (`f64::cbrt`).
387    ///
388    /// WHY not `powf(1/3)`: raising a negative base to a fractional power is
389    /// NaN, yet the real cube root of a negative number is a well-defined
390    /// negative real. We reduce to `|self|` and restore the sign via
391    /// `cbrt(-x) = -cbrt(x)`.
392    ///
393    /// WHY a division-free Newton: twofloat's `TwoFloat / TwoFloat` division and
394    /// its `powf`/`exp`/`ln` are not full double-double precision, so we refine
395    /// the *inverse* cube root `r = x^(-1/3)` with the iteration
396    /// `r <- r * (4 - x*r^3) / 3`, which uses only multiplications and an exact
397    /// `TwoFloat / f64` division by 3. Two quadratically convergent steps lift
398    /// the ~2^-53 `f64` seed to full ~2^-106 precision; then `cbrt(x) = x * r^2`.
399    #[inline]
400    fn dd_cbrt(self) -> Self {
401        if !self.0.is_finite() {
402            // cbrt(+-inf) = +-inf, cbrt(NaN) = NaN.
403            return self;
404        }
405        if self == QuadFloat::from(0.0) {
406            // Preserve the sign of zero (cbrt(-0.0) = -0.0).
407            return self;
408        }
409        let negative = self.0.is_sign_negative();
410        let x = self.0.abs();
411        let four = TwoFloat::from_f64(4.0);
412        let mut r = TwoFloat::from_f64((1.0 / x.hi()).cbrt());
413        r = r * ((four - x * (r * r * r)) / 3.0_f64);
414        r = r * ((four - x * (r * r * r)) / 3.0_f64);
415        let result = QuadFloat(x * r * r);
416        if negative { -result } else { result }
417    }
418}
419
420#[cfg(feature = "f128")]
421impl From<f64> for QuadFloat {
422    #[inline]
423    fn from(value: f64) -> Self {
424        Self(TwoFloat::from_f64(value))
425    }
426}
427
428#[cfg(feature = "f128")]
429impl From<TwoFloat> for QuadFloat {
430    #[inline]
431    fn from(value: TwoFloat) -> Self {
432        Self(value)
433    }
434}
435
436// Implement arithmetic operations by delegating to TwoFloat
437#[cfg(feature = "f128")]
438impl Add for QuadFloat {
439    type Output = Self;
440    #[inline]
441    fn add(self, rhs: Self) -> Self::Output {
442        Self(self.0 + rhs.0)
443    }
444}
445
446#[cfg(feature = "f128")]
447impl Sub for QuadFloat {
448    type Output = Self;
449    #[inline]
450    fn sub(self, rhs: Self) -> Self::Output {
451        Self(self.0 - rhs.0)
452    }
453}
454
455#[cfg(feature = "f128")]
456impl Mul for QuadFloat {
457    type Output = Self;
458    #[inline]
459    fn mul(self, rhs: Self) -> Self::Output {
460        Self(self.0 * rhs.0)
461    }
462}
463
464#[cfg(feature = "f128")]
465impl Div for QuadFloat {
466    type Output = Self;
467    #[inline]
468    fn div(self, rhs: Self) -> Self::Output {
469        Self(self.0 / rhs.0)
470    }
471}
472
473#[cfg(feature = "f128")]
474impl Neg for QuadFloat {
475    type Output = Self;
476    #[inline]
477    fn neg(self) -> Self::Output {
478        Self(-self.0)
479    }
480}
481
482#[cfg(feature = "f128")]
483impl AddAssign for QuadFloat {
484    #[inline]
485    fn add_assign(&mut self, rhs: Self) {
486        self.0 = self.0 + rhs.0;
487    }
488}
489
490#[cfg(feature = "f128")]
491impl SubAssign for QuadFloat {
492    #[inline]
493    fn sub_assign(&mut self, rhs: Self) {
494        self.0 = self.0 - rhs.0;
495    }
496}
497
498#[cfg(feature = "f128")]
499impl MulAssign for QuadFloat {
500    #[inline]
501    fn mul_assign(&mut self, rhs: Self) {
502        self.0 = self.0 * rhs.0;
503    }
504}
505
506#[cfg(feature = "f128")]
507impl DivAssign for QuadFloat {
508    #[inline]
509    fn div_assign(&mut self, rhs: Self) {
510        self.0 = self.0 / rhs.0;
511    }
512}
513
514#[cfg(feature = "f128")]
515impl Rem for QuadFloat {
516    type Output = Self;
517    #[inline]
518    fn rem(self, rhs: Self) -> Self::Output {
519        // Truncated remainder, matching Rust's `%` for primitive floats: the
520        // result carries the sign of the dividend and `|result| < |rhs|`.
521        //   r = self - trunc(self / rhs) * rhs
522        // (NOT floored division, which would give the sign of the divisor.) The
523        // quotient is truncated at double-double precision, and a single
524        // boundary correction repairs the at-most-one-ULP error the division can
525        // introduce into the truncated quotient so the two defining properties
526        // above hold exactly. For `|self / rhs|` beyond the ~2^106 integer range
527        // of double-double the quotient can no longer be represented exactly and
528        // the result degrades gracefully, as it does for any single-step fmod.
529
530        // twofloat does not define operations on non-finite values, so match
531        // f64 `%` on the special cases explicitly:
532        //   x % 0 = NaN, inf % y = NaN, x % inf = x, NaN propagates.
533        if self.0.is_nan() || rhs.0.is_nan() || self.0.is_infinite() || rhs == QuadFloat::from(0.0)
534        {
535            return QuadFloat(TwoFloat::NAN);
536        }
537        if rhs.0.is_infinite() {
538            return self;
539        }
540
541        let quotient = self / rhs;
542        let mut n = quotient.dd_trunc();
543        let mut remainder = self - n * rhs;
544
545        if remainder != QuadFloat::from(0.0) {
546            let step = if quotient > QuadFloat::from(0.0) {
547                QuadFloat::from(1.0)
548            } else {
549                QuadFloat::from(-1.0)
550            };
551            if remainder.0.is_sign_negative() != self.0.is_sign_negative() {
552                // Truncated quotient overshot: step it one unit toward zero.
553                n -= step;
554                remainder = self - n * rhs;
555            } else if QuadFloat(remainder.0.abs()) >= QuadFloat(rhs.0.abs()) {
556                // Truncated quotient fell short: step it one unit outward.
557                n += step;
558                remainder = self - n * rhs;
559            }
560        }
561        remainder
562    }
563}
564
565#[cfg(feature = "f128")]
566impl RemAssign for QuadFloat {
567    #[inline]
568    fn rem_assign(&mut self, rhs: Self) {
569        *self = *self % rhs;
570    }
571}
572
573#[cfg(feature = "f128")]
574impl Sum for QuadFloat {
575    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
576        iter.fold(QuadFloat::from(0.0), |acc, x| acc + x)
577    }
578}
579
580#[cfg(feature = "f128")]
581impl<'a> Sum<&'a QuadFloat> for QuadFloat {
582    fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
583        iter.copied().fold(QuadFloat::from(0.0), |acc, x| acc + x)
584    }
585}
586
587#[cfg(feature = "f128")]
588impl Zero for QuadFloat {
589    #[inline]
590    fn zero() -> Self {
591        QuadFloat::from(0.0)
592    }
593
594    #[inline]
595    fn is_zero(&self) -> bool {
596        self.0 == TwoFloat::from_f64(0.0)
597    }
598}
599
600#[cfg(feature = "f128")]
601impl One for QuadFloat {
602    #[inline]
603    fn one() -> Self {
604        QuadFloat::from(1.0)
605    }
606}
607
608// NumAssign is automatically derived from Num + NumAssignOps
609
610#[cfg(feature = "f128")]
611impl Display for QuadFloat {
612    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
613        write!(f, "{}", self.0)
614    }
615}
616
617// Implement Float trait for QuadFloat by delegating to TwoFloat
618#[cfg(feature = "f128")]
619impl Float for QuadFloat {
620    fn nan() -> Self {
621        QuadFloat(TwoFloat::NAN)
622    }
623
624    fn infinity() -> Self {
625        QuadFloat(TwoFloat::INFINITY)
626    }
627
628    fn neg_infinity() -> Self {
629        QuadFloat(TwoFloat::NEG_INFINITY)
630    }
631
632    fn neg_zero() -> Self {
633        QuadFloat(-TwoFloat::from_f64(0.0))
634    }
635
636    fn min_value() -> Self {
637        QuadFloat(TwoFloat::MIN)
638    }
639
640    fn min_positive_value() -> Self {
641        QuadFloat(TwoFloat::MIN_POSITIVE)
642    }
643
644    fn max_value() -> Self {
645        QuadFloat(TwoFloat::MAX)
646    }
647
648    fn is_nan(self) -> bool {
649        self.0.is_nan()
650    }
651
652    fn is_infinite(self) -> bool {
653        self.0.is_infinite()
654    }
655
656    fn is_finite(self) -> bool {
657        self.0.is_finite()
658    }
659
660    fn is_normal(self) -> bool {
661        self.0.is_normal()
662    }
663
664    fn classify(self) -> core::num::FpCategory {
665        self.0.classify()
666    }
667
668    fn floor(self) -> Self {
669        self.dd_floor()
670    }
671
672    fn ceil(self) -> Self {
673        self.dd_ceil()
674    }
675
676    fn round(self) -> Self {
677        self.dd_round()
678    }
679
680    fn trunc(self) -> Self {
681        self.dd_trunc()
682    }
683
684    fn fract(self) -> Self {
685        self.dd_fract()
686    }
687
688    fn abs(self) -> Self {
689        QuadFloat(self.0.abs())
690    }
691
692    fn signum(self) -> Self {
693        let zero = QuadFloat::from(0.0);
694        let one = QuadFloat::from(1.0);
695        if self > zero {
696            one
697        } else if self < zero {
698            -one
699        } else {
700            zero
701        }
702    }
703
704    fn is_sign_positive(self) -> bool {
705        self.0.is_sign_positive()
706    }
707
708    fn is_sign_negative(self) -> bool {
709        self.0.is_sign_negative()
710    }
711
712    fn mul_add(self, a: Self, b: Self) -> Self {
713        self * a + b
714    }
715
716    fn recip(self) -> Self {
717        QuadFloat(self.0.recip())
718    }
719
720    fn powi(self, n: i32) -> Self {
721        QuadFloat(self.0.powi(n))
722    }
723
724    fn powf(self, n: Self) -> Self {
725        QuadFloat(self.0.powf(n.0))
726    }
727
728    fn sqrt(self) -> Self {
729        QuadFloat(self.0.sqrt())
730    }
731
732    fn exp(self) -> Self {
733        QuadFloat(self.0.exp())
734    }
735
736    fn exp2(self) -> Self {
737        QuadFloat(TwoFloat::from_f64(2.0).powf(self.0))
738    }
739
740    fn ln(self) -> Self {
741        QuadFloat(self.0.ln())
742    }
743
744    fn log(self, base: Self) -> Self {
745        QuadFloat(self.0.ln() / base.0.ln())
746    }
747
748    fn log2(self) -> Self {
749        QuadFloat(self.0.ln() / TwoFloat::from_f64(2.0).ln())
750    }
751
752    fn log10(self) -> Self {
753        QuadFloat(self.0.log10())
754    }
755
756    fn max(self, other: Self) -> Self {
757        if self > other { self } else { other }
758    }
759
760    fn min(self, other: Self) -> Self {
761        if self < other { self } else { other }
762    }
763
764    fn abs_sub(self, other: Self) -> Self {
765        if self > other {
766            self - other
767        } else {
768            QuadFloat::from(0.0)
769        }
770    }
771
772    fn cbrt(self) -> Self {
773        self.dd_cbrt()
774    }
775
776    fn hypot(self, other: Self) -> Self {
777        self.dd_hypot(other)
778    }
779
780    fn sin(self) -> Self {
781        QuadFloat(self.0.sin())
782    }
783
784    fn cos(self) -> Self {
785        QuadFloat(self.0.cos())
786    }
787
788    fn tan(self) -> Self {
789        QuadFloat(self.0.tan())
790    }
791
792    fn asin(self) -> Self {
793        QuadFloat(self.0.asin())
794    }
795
796    fn acos(self) -> Self {
797        QuadFloat(self.0.acos())
798    }
799
800    fn atan(self) -> Self {
801        QuadFloat(self.0.atan())
802    }
803
804    fn atan2(self, other: Self) -> Self {
805        QuadFloat(self.0.atan2(other.0))
806    }
807
808    fn sin_cos(self) -> (Self, Self) {
809        let (sin, cos) = self.0.sin_cos();
810        (QuadFloat(sin), QuadFloat(cos))
811    }
812
813    fn exp_m1(self) -> Self {
814        QuadFloat(self.0.exp() - TwoFloat::from_f64(1.0))
815    }
816
817    fn ln_1p(self) -> Self {
818        QuadFloat((self.0 + TwoFloat::from_f64(1.0)).ln())
819    }
820
821    fn sinh(self) -> Self {
822        QuadFloat(self.0.sinh())
823    }
824
825    fn cosh(self) -> Self {
826        QuadFloat(self.0.cosh())
827    }
828
829    fn tanh(self) -> Self {
830        QuadFloat(self.0.tanh())
831    }
832
833    fn asinh(self) -> Self {
834        QuadFloat(self.0.asinh())
835    }
836
837    fn acosh(self) -> Self {
838        QuadFloat(self.0.acosh())
839    }
840
841    fn atanh(self) -> Self {
842        QuadFloat(self.0.atanh())
843    }
844
845    fn integer_decode(self) -> (u64, i16, i8) {
846        // For double-double, we decode the high part
847        self.0.hi().integer_decode()
848    }
849
850    fn epsilon() -> Self {
851        QuadFloat::from(f64::EPSILON) * QuadFloat::from(f64::EPSILON)
852    }
853
854    fn to_degrees(self) -> Self {
855        const FACTOR: f64 = 180.0 / core::f64::consts::PI;
856        self * QuadFloat::from(FACTOR)
857    }
858
859    fn to_radians(self) -> Self {
860        const FACTOR: f64 = core::f64::consts::PI / 180.0;
861        self * QuadFloat::from(FACTOR)
862    }
863}
864
865#[cfg(feature = "f128")]
866impl FromPrimitive for QuadFloat {
867    fn from_i64(n: i64) -> Option<Self> {
868        Some(QuadFloat::from(n as f64))
869    }
870
871    fn from_u64(n: u64) -> Option<Self> {
872        Some(QuadFloat::from(n as f64))
873    }
874
875    fn from_f64(n: f64) -> Option<Self> {
876        Some(QuadFloat::from(n))
877    }
878}
879
880#[cfg(feature = "f128")]
881impl num_traits::Num for QuadFloat {
882    type FromStrRadixErr = num_traits::ParseFloatError;
883
884    fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
885        f64::from_str_radix(str, radix)
886            .map(QuadFloat::from)
887            .map_err(|_| num_traits::ParseFloatError {
888                kind: num_traits::FloatErrorKind::Invalid,
889            })
890    }
891}
892
893#[cfg(feature = "f128")]
894impl num_traits::NumCast for QuadFloat {
895    fn from<T: num_traits::ToPrimitive>(n: T) -> Option<Self> {
896        n.to_f64().map(<QuadFloat as From<f64>>::from)
897    }
898}
899
900#[cfg(feature = "f128")]
901impl num_traits::ToPrimitive for QuadFloat {
902    fn to_i64(&self) -> Option<i64> {
903        self.0.hi().to_i64()
904    }
905
906    fn to_u64(&self) -> Option<u64> {
907        self.0.hi().to_u64()
908    }
909
910    fn to_f64(&self) -> Option<f64> {
911        Some(self.0.hi())
912    }
913}
914
915// =============================================================================
916// Scalar/Real/Field implementations for QuadFloat
917// =============================================================================
918
919#[cfg(feature = "f128")]
920impl Scalar for QuadFloat {
921    type Real = QuadFloat;
922
923    #[inline]
924    fn abs(self) -> Self::Real {
925        QuadFloat(self.0.abs())
926    }
927
928    #[inline]
929    fn conj(self) -> Self {
930        self
931    }
932
933    #[inline]
934    fn is_real() -> bool {
935        true
936    }
937
938    #[inline]
939    fn real(self) -> Self::Real {
940        self
941    }
942
943    #[inline]
944    fn imag(self) -> Self::Real {
945        QuadFloat::from(0.0)
946    }
947
948    #[inline]
949    fn from_real_imag(re: Self::Real, _im: Self::Real) -> Self {
950        re
951    }
952
953    #[inline]
954    fn abs_sq(self) -> Self::Real {
955        self * self
956    }
957
958    #[inline]
959    fn epsilon() -> Self::Real {
960        // Double-double epsilon is approximately 2^-106
961        QuadFloat::from(f64::EPSILON) * QuadFloat::from(f64::EPSILON)
962    }
963
964    #[inline]
965    fn min_positive() -> Self::Real {
966        QuadFloat::from(f64::MIN_POSITIVE)
967    }
968
969    #[inline]
970    fn max_value() -> Self::Real {
971        QuadFloat::from(f64::MAX)
972    }
973}
974
975#[cfg(feature = "f128")]
976impl Real for QuadFloat {
977    #[inline]
978    fn sqrt(self) -> Self {
979        QuadFloat(self.0.sqrt())
980    }
981
982    #[inline]
983    fn ln(self) -> Self {
984        QuadFloat(self.0.ln())
985    }
986
987    #[inline]
988    fn exp(self) -> Self {
989        QuadFloat(self.0.exp())
990    }
991
992    #[inline]
993    fn sin(self) -> Self {
994        QuadFloat(self.0.sin())
995    }
996
997    #[inline]
998    fn cos(self) -> Self {
999        QuadFloat(self.0.cos())
1000    }
1001
1002    #[inline]
1003    fn atan2(self, other: Self) -> Self {
1004        QuadFloat(self.0.atan2(other.0))
1005    }
1006
1007    #[inline]
1008    fn powf(self, n: Self) -> Self {
1009        QuadFloat(self.0.powf(n.0))
1010    }
1011
1012    #[inline]
1013    fn signum(self) -> Self {
1014        let zero = QuadFloat::from(0.0);
1015        let one = QuadFloat::from(1.0);
1016        if self > zero {
1017            one
1018        } else if self < zero {
1019            -one
1020        } else {
1021            zero
1022        }
1023    }
1024
1025    #[inline]
1026    fn mul_add(self, a: Self, b: Self) -> Self {
1027        // TwoFloat doesn't have mul_add, so implement manually
1028        self * a + b
1029    }
1030
1031    #[inline]
1032    fn floor(self) -> Self {
1033        self.dd_floor()
1034    }
1035
1036    #[inline]
1037    fn ceil(self) -> Self {
1038        self.dd_ceil()
1039    }
1040
1041    #[inline]
1042    fn round(self) -> Self {
1043        self.dd_round()
1044    }
1045
1046    #[inline]
1047    fn trunc(self) -> Self {
1048        self.dd_trunc()
1049    }
1050
1051    #[inline]
1052    fn hypot(self, other: Self) -> Self {
1053        self.dd_hypot(other)
1054    }
1055}
1056
1057#[cfg(feature = "f128")]
1058impl Field for QuadFloat {
1059    #[inline]
1060    fn mul_conj(self, other: Self) -> Self {
1061        self * other
1062    }
1063
1064    #[inline]
1065    fn conj_mul(self, other: Self) -> Self {
1066        self * other
1067    }
1068
1069    #[inline]
1070    fn recip(self) -> Self {
1071        QuadFloat(self.0.recip())
1072    }
1073
1074    #[inline]
1075    fn powi(self, n: i32) -> Self {
1076        QuadFloat(self.0.powi(n))
1077    }
1078}