Skip to main content

dashu_float/
repr.rs

1use crate::{
2    error::{assert_finite, FpError},
3    round::{Round, Rounded},
4    utils::{digit_len, split_digits, split_digits_ref},
5};
6use core::marker::PhantomData;
7use dashu_base::{Approximation::*, EstimatedLog2, Sign};
8pub use dashu_int::Word;
9use dashu_int::{DoubleWord, IBig, UBig};
10
11/// Underlying representation of an arbitrary precision floating number.
12///
13/// The floating point number is represented as `significand * base^exponent`, where the
14/// type of the significand is [IBig], and the type of exponent is [isize]. The representation
15/// is always normalized (nonzero signficand is not divisible by the base, or zero signficand
16/// with zero exponent).
17///
18/// When it's used together with a [Context], its precision will be limited so that
19/// `|significand| < base^precision`. As an intentional exception, the result of an inexact
20/// addition or subtraction may carry one extra guard digit, so `|significand|` can be up to
21/// `base^(precision+1)`; the guard digit is what lets a much-smaller operand be reduced to a
22/// sign-only sticky bit during alignment without mis-rounding.
23///
24/// # Infinity and signed zero
25///
26/// Special values are encoded with a zero significand and a sentinel exponent:
27/// - value zero (`+0`): exponent = 0
28/// - negative zero (`-0`): exponent = -1
29/// - positive infinity (`+inf`): exponent = `isize::MAX`
30/// - negative infinity (`-inf`): exponent = `isize::MIN`
31///
32/// The infinities are only supposed to be consumed as sentinels: only equality test and
33/// comparison are implemented for them, and any arithmetic operation that takes an infinity
34/// as input will lead to panic (at the `FBig` layer) or return an error (at the `Context`
35/// layer). If an operation result is too large or too small, the operation will return an
36/// infinity (as a value) at the `Context` layer, or panic at the `FBig` layer.
37///
38pub struct Repr<const BASE: Word> {
39    /// The significand of the floating point number. If the significand is zero, then the
40    /// number is a special value identified by the exponent (see the struct-level docs):
41    /// `+0`, `-0`, `+inf`, or `-inf`.
42    pub(crate) significand: IBig,
43
44    /// The exponent of the floating point number.
45    pub(crate) exponent: isize,
46}
47
48impl<const B: Word> PartialEq for Repr<B> {
49    /// Two representations are equal when they denote the same value. In particular `+0`
50    /// and `-0` compare equal, as do two infinities of the same sign.
51    #[inline]
52    fn eq(&self, other: &Self) -> bool {
53        if self.significand.is_zero() && other.significand.is_zero() {
54            let (self_inf, other_inf) = (self.is_infinite(), other.is_infinite());
55            match (self_inf, other_inf) {
56                (true, true) => self.sign() == other.sign(),
57                (false, false) => true, // both are ±0
58                _ => false,             // one is zero, the other is infinite
59            }
60        } else {
61            self.significand == other.significand && self.exponent == other.exponent
62        }
63    }
64}
65
66impl<const B: Word> Eq for Repr<B> {}
67
68/// The context containing runtime information for the floating point number and its operations.
69///
70/// The context currently consists of a *precision limit* and a *rounding mode*. All the operation
71/// associated with the context will be precise to the **full precision** (`|error| < 1 ulp`).
72/// The rounding result returned from the functions tells additional error information, see
73/// [the rounding mode module][crate::round::mode] for details.
74///
75/// # Precision
76///
77/// The precision limit determine the number of significant digits in the float number.
78///
79/// For binary operations, the result will have the higher one between the precisions of two
80/// operands.
81///
82/// If the precision is set to 0, then the precision is **unlimited** during operations.
83/// Be cautious to use unlimited precision because it can leads to very huge significands.
84/// Unlimited precision is forbidden for some operations where the result is always inexact.
85///
86/// # Rounding Mode
87///
88/// The rounding mode determines the rounding behavior of the float operations.
89///
90/// See [the rounding mode module][crate::round::mode] for built-in rounding modes.
91/// Users can implement custom rounding mode by implementing the [Round][crate::round::Round]
92/// trait, but this is discouraged since in the future we might restrict the rounding
93/// modes to be chosen from the the built-in modes.
94///
95/// For binary operations, the two oprands must have the same rounding mode.
96///
97#[derive(Clone, Copy)]
98pub struct Context<RoundingMode: Round> {
99    /// The precision of the floating point number.
100    /// If set to zero, then the precision is unlimited.
101    pub(crate) precision: usize,
102    _marker: PhantomData<RoundingMode>,
103}
104
105/// Flip the sign of a special-value exponent: `+0 (0) <-> -0 (-1)`, `+inf (MAX) <-> -inf (MIN)`.
106/// For any other (non-canonical) exponent the plain negation is used, which is safe because such
107/// values have magnitude strictly less than `isize::MAX`.
108#[inline]
109const fn negate_special_exponent(exp: isize) -> isize {
110    match exp {
111        0 => -1,
112        -1 => 0,
113        isize::MAX => isize::MIN,
114        isize::MIN => isize::MAX,
115        other => -other,
116    }
117}
118
119/// Build a `Repr` from a rounded significand, preserving the input sign when rounding
120/// produces zero (`significand * B^exponent` where the significand collapsed to `+0`).
121fn rounded_to_repr<const B: Word>(
122    significand: IBig,
123    exponent: isize,
124    input_negative: bool,
125) -> Repr<B> {
126    if significand.is_zero() && input_negative {
127        Repr::neg_zero()
128    } else {
129        Repr::new(significand, exponent)
130    }
131}
132
133/// Normalize a `(significand, exponent)` pair for base `B`: fold trailing base-`B` zero-digits
134/// from the significand into the exponent, and return the number of significant base-`B` digits.
135///
136/// The return triple is all-`Copy`, so it can be destructured in a `const fn` — unlike a pair
137/// carrying a [`Repr`], whose heap drop can't be const-evaluated. It is the shared core of
138/// [`Repr::new_const`] and [`crate::FBig::from_parts_const`].
139pub(crate) const fn normalize_word_const<const B: Word>(
140    mut significand: DoubleWord,
141    mut exponent: isize,
142) -> (DoubleWord, isize, usize) {
143    if significand == 0 {
144        return (0, exponent, 0);
145    }
146
147    let mut digits = 0;
148    if B.is_power_of_two() {
149        let base_bits = B.trailing_zeros();
150        let shift = significand.trailing_zeros() / base_bits;
151        significand >>= shift * base_bits;
152        exponent += shift as isize;
153        digits =
154            ((DoubleWord::BITS - significand.leading_zeros() + base_bits - 1) / base_bits) as usize;
155    } else {
156        let mut pow: DoubleWord = 1;
157        while significand % (B as DoubleWord) == 0 {
158            significand /= B as DoubleWord;
159            exponent += 1;
160        }
161        while let Some(next) = pow.checked_mul(B as DoubleWord) {
162            digits += 1;
163            if next > significand {
164                break;
165            }
166            pow = next;
167        }
168    }
169    (significand, exponent, digits)
170}
171
172impl<const B: Word> Repr<B> {
173    /// The base of the representation. It's exposed as an [IBig] constant.
174    pub const BASE: UBig = UBig::from_word(B);
175
176    /// Create a [Repr] instance representing value zero
177    #[inline]
178    pub const fn zero() -> Self {
179        Self {
180            significand: IBig::ZERO,
181            exponent: 0,
182        }
183    }
184    /// Create a [Repr] instance representing value one
185    #[inline]
186    pub const fn one() -> Self {
187        Self {
188            significand: IBig::ONE,
189            exponent: 0,
190        }
191    }
192    /// Create a [Repr] instance representing value negative one
193    #[inline]
194    pub const fn neg_one() -> Self {
195        Self {
196            significand: IBig::NEG_ONE,
197            exponent: 0,
198        }
199    }
200    /// Create a [Repr] instance representing the (positive) infinity
201    #[inline]
202    pub const fn infinity() -> Self {
203        Self {
204            significand: IBig::ZERO,
205            exponent: isize::MAX,
206        }
207    }
208    /// Create a [Repr] instance representing the negative infinity
209    #[inline]
210    pub const fn neg_infinity() -> Self {
211        Self {
212            significand: IBig::ZERO,
213            exponent: isize::MIN,
214        }
215    }
216    /// Create a [Repr] instance representing the negative zero (`-0`)
217    ///
218    /// Negative zero is produced by operations (e.g. `1 / -inf`, `ceil(-0)`, cancellation
219    /// under round-toward-negative) and is distinct from `+0` only in operations that are
220    /// sensitive to the sign of zero (e.g. `1 / -0 = -inf`). It compares equal to `+0`.
221    #[inline]
222    pub const fn neg_zero() -> Self {
223        Self {
224            significand: IBig::ZERO,
225            exponent: -1,
226        }
227    }
228
229    /// Determine if the [Repr] represents positive zero (`+0`)
230    ///
231    /// This returns `true` only for `+0`; use [`Self::is_neg_zero`] to detect `-0`, or check
232    /// `self.significand().is_zero()` to detect either signed zero.
233    ///
234    /// # Examples
235    ///
236    /// ```
237    /// # use dashu_float::Repr;
238    /// assert!(Repr::<2>::zero().is_pos_zero());
239    /// assert!(!Repr::<10>::neg_zero().is_pos_zero());
240    /// assert!(!Repr::<10>::one().is_pos_zero());
241    /// ```
242    #[inline]
243    pub const fn is_pos_zero(&self) -> bool {
244        self.significand.is_zero() && self.exponent == 0
245    }
246
247    /// Determine if the [Repr] represents the negative zero (`-0`)
248    ///
249    /// # Examples
250    ///
251    /// ```
252    /// # use dashu_float::Repr;
253    /// assert!(Repr::<2>::neg_zero().is_neg_zero());
254    /// assert!(!Repr::<10>::zero().is_neg_zero());
255    /// assert!(!Repr::<10>::one().is_neg_zero());
256    /// ```
257    #[inline]
258    pub const fn is_neg_zero(&self) -> bool {
259        self.significand.is_zero() && self.exponent == -1
260    }
261
262    /// Determine if the [Repr] represents one
263    ///
264    /// # Examples
265    ///
266    /// ```
267    /// # use dashu_float::Repr;
268    /// assert!(Repr::<2>::zero().is_pos_zero());
269    /// assert!(!Repr::<10>::one().is_pos_zero());
270    /// ```
271    #[inline]
272    pub const fn is_one(&self) -> bool {
273        self.significand.is_one() && self.exponent == 0
274    }
275
276    /// Determine if the [Repr] represents the (±)infinity
277    ///
278    /// # Examples
279    ///
280    /// ```
281    /// # use dashu_float::Repr;
282    /// assert!(Repr::<2>::infinity().is_infinite());
283    /// assert!(Repr::<10>::neg_infinity().is_infinite());
284    /// assert!(!Repr::<10>::one().is_infinite());
285    /// assert!(!Repr::<10>::neg_zero().is_infinite());
286    /// ```
287    #[inline]
288    pub const fn is_infinite(&self) -> bool {
289        self.significand.is_zero() && (self.exponent == isize::MAX || self.exponent == isize::MIN)
290    }
291
292    /// Determine if the [Repr] represents a finite number
293    ///
294    /// # Examples
295    ///
296    /// ```
297    /// # use dashu_float::Repr;
298    /// assert!(Repr::<2>::zero().is_finite());
299    /// assert!(Repr::<10>::one().is_finite());
300    /// assert!(!Repr::<16>::infinity().is_finite());
301    /// ```
302    #[inline]
303    pub const fn is_finite(&self) -> bool {
304        !self.is_infinite()
305    }
306
307    /// Determine if the number can be regarded as an integer.
308    ///
309    /// Note that this function returns false when the number is infinite.
310    ///
311    /// # Examples
312    ///
313    /// ```
314    /// # use dashu_float::Repr;
315    /// assert!(Repr::<2>::zero().is_int());
316    /// assert!(Repr::<10>::one().is_int());
317    /// assert!(!Repr::<16>::new(123.into(), -1).is_int());
318    /// ```
319    pub fn is_int(&self) -> bool {
320        if self.is_infinite() {
321            false
322        } else {
323            self.exponent >= 0
324        }
325    }
326
327    /// Get the sign of the number
328    ///
329    /// Note that `-0` has a negative sign (so `1 / -0 = -inf`), while `+0` has a positive sign.
330    ///
331    /// # Examples
332    ///
333    /// ```
334    /// # use dashu_base::Sign;
335    /// # use dashu_float::Repr;
336    /// assert_eq!(Repr::<2>::zero().sign(), Sign::Positive);
337    /// assert_eq!(Repr::<2>::neg_zero().sign(), Sign::Negative);
338    /// assert_eq!(Repr::<2>::neg_one().sign(), Sign::Negative);
339    /// assert_eq!(Repr::<10>::neg_infinity().sign(), Sign::Negative);
340    /// ```
341    #[inline]
342    pub const fn sign(&self) -> Sign {
343        if self.significand.is_zero() {
344            if self.exponent >= 0 {
345                Sign::Positive
346            } else {
347                Sign::Negative
348            }
349        } else {
350            self.significand.sign()
351        }
352    }
353
354    /// Negate the number, correctly toggling the sign of `±0` and `±inf` by flipping the
355    /// special-value exponent (negating the significand alone is a no-op for zero).
356    #[inline]
357    pub(crate) fn neg(self) -> Self {
358        if self.significand.is_zero() {
359            Self {
360                significand: self.significand,
361                exponent: negate_special_exponent(self.exponent),
362            }
363        } else {
364            Self {
365                significand: -self.significand,
366                exponent: self.exponent,
367            }
368        }
369    }
370
371    /// Check that a `Repr` with a non-zero significand has a valid finite exponent.
372    ///
373    /// Returns [`FpError::Overflow`] or [`FpError::Underflow`] when the exponent collides with
374    /// the `+inf`/`-inf` sentinels (`isize::MAX` / `isize::MIN`). Zero-significand reprs
375    /// (canonical special values) always pass.
376    pub(crate) fn check_finite_exponent(self) -> Result<Self, FpError> {
377        if !self.significand.is_zero() {
378            if self.exponent == isize::MAX {
379                Err(FpError::Overflow(self.sign()))
380            } else if self.exponent == isize::MIN {
381                Err(FpError::Underflow(self.sign()))
382            } else {
383                Ok(self)
384            }
385        } else {
386            Ok(self)
387        }
388    }
389
390    /// Create the `Repr` for a signed infinity from the mathematical sign of a result that
391    /// overflowed.
392    #[inline]
393    pub(crate) const fn infinity_with_sign(sign: Sign) -> Self {
394        match sign {
395            Sign::Positive => Self::infinity(),
396            Sign::Negative => Self::neg_infinity(),
397        }
398    }
399
400    /// Create the `Repr` for a signed zero from the mathematical sign of a result that
401    /// underflowed.
402    #[inline]
403    pub(crate) const fn zero_with_sign(sign: Sign) -> Self {
404        match sign {
405            Sign::Positive => Self::zero(),
406            Sign::Negative => Self::neg_zero(),
407        }
408    }
409
410    /// Normalize the float representation so that the significand is not divisible by the base.
411    ///
412    /// A zero significand denotes a canonical special value (`+0`, `-0`, `+inf`, `-inf`) and is
413    /// returned unchanged; any other (non-canonical) zero significand is normalized to `+0`.
414    pub(crate) fn normalize(self) -> Self {
415        if self.significand.is_zero() {
416            // Preserve the four canonical special-value encodings; collapse anything else to +0.
417            if self.exponent == 0
418                || self.exponent == -1
419                || self.exponent == isize::MAX
420                || self.exponent == isize::MIN
421            {
422                return self;
423            }
424            return Self::zero();
425        }
426
427        let Self {
428            mut significand,
429            mut exponent,
430        } = self;
431        if B == 2 {
432            let shift = significand.trailing_zeros().unwrap();
433            significand >>= shift;
434            exponent = exponent.saturating_add(shift as isize);
435        } else if B.is_power_of_two() {
436            let bits = B.trailing_zeros() as usize;
437            let shift = significand.trailing_zeros().unwrap() / bits;
438            significand >>= shift * bits;
439            exponent = exponent.saturating_add(shift as isize);
440        } else {
441            let (sign, mut mag) = significand.into_parts();
442            let shift = mag.remove(&UBig::from_word(B)).unwrap();
443            exponent = exponent.saturating_add(shift as isize);
444            significand = IBig::from_parts(sign, mag);
445        }
446        Self {
447            significand,
448            exponent,
449        }
450    }
451
452    /// Get the number of digits (under base `B`) in the significand.
453    ///
454    /// If the number is 0, then 0 is returned (instead of 1).
455    ///
456    /// # Examples
457    ///
458    /// ```
459    /// # use dashu_float::Repr;
460    /// assert_eq!(Repr::<2>::zero().digits(), 0);
461    /// assert_eq!(Repr::<2>::one().digits(), 1);
462    /// assert_eq!(Repr::<10>::one().digits(), 1);
463    ///
464    /// assert_eq!(Repr::<10>::new(100.into(), 0).digits(), 1); // 1e2
465    /// assert_eq!(Repr::<10>::new(101.into(), 0).digits(), 3);
466    /// ```
467    #[inline]
468    pub fn digits(&self) -> usize {
469        assert_finite(self);
470        digit_len::<B>(&self.significand)
471    }
472
473    /// Fast over-estimation of [digits][Self::digits]
474    ///
475    /// # Examples
476    ///
477    /// ```
478    /// # use dashu_float::Repr;
479    /// assert_eq!(Repr::<2>::zero().digits_ub(), 0);
480    /// assert_eq!(Repr::<2>::one().digits_ub(), 1);
481    /// assert_eq!(Repr::<10>::one().digits_ub(), 1);
482    /// assert_eq!(Repr::<2>::new(31.into(), 0).digits_ub(), 5);
483    /// assert_eq!(Repr::<10>::new(99.into(), 0).digits_ub(), 2);
484    /// ```
485    #[inline]
486    pub fn digits_ub(&self) -> usize {
487        assert_finite(self);
488        if self.significand.is_zero() {
489            return 0;
490        }
491
492        let log = match B {
493            2 => self.significand.log2_bounds().1,
494            10 => self.significand.log2_bounds().1 * core::f32::consts::LOG10_2,
495            _ => self.significand.log2_bounds().1 / Self::BASE.log2_bounds().0,
496        };
497        log as usize + 1
498    }
499
500    /// Fast under-estimation of [digits][Self::digits]
501    ///
502    /// # Examples
503    ///
504    /// ```
505    /// # use dashu_float::Repr;
506    /// assert_eq!(Repr::<2>::zero().digits_lb(), 0);
507    /// assert_eq!(Repr::<2>::one().digits_lb(), 0);
508    /// assert_eq!(Repr::<10>::one().digits_lb(), 0);
509    /// assert!(Repr::<10>::new(1001.into(), 0).digits_lb() <= 3);
510    /// ```
511    #[inline]
512    pub fn digits_lb(&self) -> usize {
513        assert_finite(self);
514        if self.significand.is_zero() {
515            return 0;
516        }
517
518        let log = match B {
519            2 => self.significand.log2_bounds().0,
520            10 => self.significand.log2_bounds().0 * core::f32::consts::LOG10_2,
521            _ => self.significand.log2_bounds().0 / Self::BASE.log2_bounds().1,
522        };
523        log as usize
524    }
525
526    /// Quickly test if `|self| < 1`. IT's not always correct,
527    /// but there are guaranteed to be no false postives.
528    #[inline]
529    pub(crate) fn smaller_than_one(&self) -> bool {
530        debug_assert!(self.is_finite());
531        self.exponent + (self.digits_ub() as isize) < -1
532    }
533
534    /// Create a [Repr] from the significand and exponent. This
535    /// constructor will normalize the representation.
536    ///
537    /// # Examples
538    ///
539    /// ```
540    /// # use dashu_int::IBig;
541    /// # use dashu_float::Repr;
542    /// let a = Repr::<2>::new(400.into(), -2);
543    /// assert_eq!(a.significand(), &IBig::from(25));
544    /// assert_eq!(a.exponent(), 2);
545    ///
546    /// let b = Repr::<10>::new(400.into(), -2);
547    /// assert_eq!(b.significand(), &IBig::from(4));
548    /// assert_eq!(b.exponent(), 0);
549    /// ```
550    #[inline]
551    pub fn new(significand: IBig, exponent: isize) -> Self {
552        Self {
553            significand,
554            exponent,
555        }
556        .normalize()
557    }
558
559    /// Create a normalized [`Repr`] from a signed [`DoubleWord`] significand and an exponent.
560    ///
561    /// This is the const-evaluable counterpart of [`Repr::new`]: because it operates on a
562    /// [`DoubleWord`] significand it needs no heap `IBig` arithmetic, so it is usable in `const`
563    /// contexts — it is what the complex literal macros build on. As with [`Repr::new`], the
564    /// significand is normalized (trailing zero digits in base `B` are folded into the exponent).
565    /// A zero significand yields [`Repr::zero`] regardless of sign.
566    ///
567    /// Unlike [`Repr::new`], this does not report the digit count (computing it would require
568    /// returning it alongside the `Repr`, which can't be destructured in a `const fn`); callers
569    /// that need a precision should pass it explicitly.
570    ///
571    /// # Examples
572    ///
573    /// ```
574    /// use dashu_base::Sign;
575    /// use dashu_float::Repr;
576    /// use dashu_int::IBig;
577    ///
578    /// let r = Repr::<10>::new_const(Sign::Positive, 123400, -2);
579    /// assert_eq!(r.significand(), &IBig::from(1234));
580    /// assert_eq!(r.exponent(), 0);
581    /// ```
582    #[inline]
583    pub const fn new_const(sign: Sign, significand: DoubleWord, exponent: isize) -> Self {
584        let (significand, exponent, _) = normalize_word_const::<B>(significand, exponent);
585        if significand == 0 {
586            Self::zero()
587        } else {
588            Self {
589                significand: IBig::from_parts_const(sign, significand),
590                exponent,
591            }
592        }
593    }
594
595    /// Get the significand of the representation
596    #[inline]
597    pub fn significand(&self) -> &IBig {
598        &self.significand
599    }
600
601    /// Get the exponent of the representation
602    #[inline]
603    pub fn exponent(&self) -> isize {
604        self.exponent
605    }
606
607    /// Convert the float number into raw `(signficand, exponent)` parts
608    ///
609    /// # Examples
610    ///
611    /// ```
612    /// # use dashu_float::Repr;
613    /// use dashu_int::IBig;
614    ///
615    /// let a = Repr::<2>::new(400.into(), -2);
616    /// assert_eq!(a.into_parts(), (IBig::from(25), 2));
617    ///
618    /// let b = Repr::<10>::new(400.into(), -2);
619    /// assert_eq!(b.into_parts(), (IBig::from(4), 0));
620    /// ```
621    #[inline]
622    pub fn into_parts(self) -> (IBig, isize) {
623        (self.significand, self.exponent)
624    }
625
626    /// Create an Repr from a static sequence of [Word][crate::Word]s representing the significand.
627    ///
628    /// This method is intended for static creation macros.
629    #[doc(hidden)]
630    #[rustversion::since(1.64)]
631    #[inline]
632    pub const unsafe fn from_static_words(
633        sign: Sign,
634        significand: &'static [Word],
635        exponent: isize,
636    ) -> Self {
637        let significand = IBig::from_static_words(sign, significand);
638        assert!(!significand.is_multiple_of_const(B as _));
639
640        Self {
641            significand,
642            exponent,
643        }
644    }
645}
646
647// This custom implementation is necessary due to https://github.com/rust-lang/rust/issues/98374
648impl<const B: Word> Clone for Repr<B> {
649    #[inline]
650    fn clone(&self) -> Self {
651        Self {
652            significand: self.significand.clone(),
653            exponent: self.exponent,
654        }
655    }
656
657    #[inline]
658    fn clone_from(&mut self, source: &Self) {
659        self.significand.clone_from(&source.significand);
660        self.exponent = source.exponent;
661    }
662}
663
664impl<R: Round> Context<R> {
665    /// Create a float operation context with the given precision limit.
666    #[inline]
667    pub const fn new(precision: usize) -> Self {
668        Self {
669            precision,
670            _marker: PhantomData,
671        }
672    }
673
674    /// Create a float operation context with the higher precision from the two context inputs.
675    ///
676    /// # Examples
677    ///
678    /// ```
679    /// use dashu_float::{Context, round::mode::Zero};
680    ///
681    /// let ctxt1 = Context::<Zero>::new(2);
682    /// let ctxt2 = Context::<Zero>::new(5);
683    /// assert_eq!(Context::max(ctxt1, ctxt2).precision(), 5);
684    /// ```
685    #[inline]
686    pub const fn max(lhs: Self, rhs: Self) -> Self {
687        Self {
688            // this comparison also correctly handles ulimited precisions (precision = 0)
689            precision: if lhs.precision > rhs.precision {
690                lhs.precision
691            } else {
692                rhs.precision
693            },
694            _marker: PhantomData,
695        }
696    }
697
698    /// Check whether the precision is limited (not zero)
699    #[inline]
700    pub(crate) const fn is_limited(&self) -> bool {
701        self.precision != 0
702    }
703
704    /// Get the precision limited from the context
705    #[inline]
706    pub const fn precision(&self) -> usize {
707        self.precision
708    }
709
710    /// Round the repr to the desired precision
711    pub(crate) fn repr_round<const B: Word>(&self, repr: Repr<B>) -> Rounded<Repr<B>> {
712        assert_finite(&repr);
713        if !self.is_limited() {
714            return Exact(repr);
715        }
716
717        let digits = repr.digits();
718        if digits > self.precision {
719            let shift = digits - self.precision;
720            let input_neg = repr.sign() == Sign::Negative;
721            let (signif_hi, signif_lo) = split_digits::<B>(repr.significand, shift);
722            let adjust = R::round_fract::<B>(&signif_hi, signif_lo, shift);
723            let sig = signif_hi + adjust;
724            let result = rounded_to_repr(sig, repr.exponent + shift as isize, input_neg);
725            Inexact(result, adjust)
726        } else {
727            Exact(repr)
728        }
729    }
730
731    /// Round the repr to the desired precision
732    pub(crate) fn repr_round_ref<const B: Word>(&self, repr: &Repr<B>) -> Rounded<Repr<B>> {
733        assert_finite(repr);
734        if !self.is_limited() {
735            return Exact(repr.clone());
736        }
737
738        let digits = repr.digits();
739        if digits > self.precision {
740            let shift = digits - self.precision;
741            let input_neg = repr.sign() == Sign::Negative;
742            let (signif_hi, signif_lo) = split_digits_ref::<B>(&repr.significand, shift);
743            let adjust = R::round_fract::<B>(&signif_hi, signif_lo, shift);
744            let sig = signif_hi + adjust;
745            let result = rounded_to_repr(sig, repr.exponent + shift as isize, input_neg);
746            Inexact(result, adjust)
747        } else {
748            Exact(repr.clone())
749        }
750    }
751}
752
753#[cfg(test)]
754mod tests {
755    use super::*;
756    use dashu_base::Sign;
757
758    #[test]
759    fn infinity_encoding() {
760        assert_eq!(Repr::<2>::infinity().exponent, isize::MAX);
761        assert_eq!(Repr::<10>::neg_infinity().exponent, isize::MIN);
762        assert!(Repr::<2>::infinity().is_infinite());
763        assert!(Repr::<10>::neg_infinity().is_infinite());
764        assert!(!Repr::<2>::infinity().is_finite());
765        assert_eq!(Repr::<2>::infinity().sign(), Sign::Positive);
766        assert_eq!(Repr::<10>::neg_infinity().sign(), Sign::Negative);
767    }
768
769    #[test]
770    fn neg_zero_encoding() {
771        assert_eq!(Repr::<2>::neg_zero().exponent, -1);
772        assert!(Repr::<2>::neg_zero().is_neg_zero());
773        assert!(!Repr::<2>::neg_zero().is_pos_zero());
774        assert!(!Repr::<2>::neg_zero().is_infinite());
775        assert_eq!(Repr::<2>::neg_zero().sign(), Sign::Negative);
776        assert_eq!(Repr::<2>::zero().sign(), Sign::Positive);
777    }
778
779    #[test]
780    fn normalize_preserves_specials() {
781        // infinities are preserved (the previous clobbering bug)
782        assert_eq!(Repr::<2>::infinity(), Repr::<2>::infinity().normalize());
783        assert_eq!(Repr::<10>::neg_infinity(), Repr::<10>::neg_infinity().normalize());
784        // +0 is preserved
785        assert_eq!(Repr::<2>::zero(), Repr::<2>::zero().normalize());
786        // a stray zero significand with a non-sentinel exponent collapses to +0
787        let stray: Repr<2> = Repr {
788            significand: IBig::ZERO,
789            exponent: 7,
790        };
791        assert_eq!(Repr::<2>::zero(), stray.normalize());
792        // non-zero significands are still normalized
793        let r: Repr<2> = Repr {
794            significand: IBig::from(0b10100i32),
795            exponent: 0,
796        };
797        let r = r.normalize();
798        assert_eq!(r.significand, IBig::from(0b101i32));
799        assert_eq!(r.exponent, 2);
800    }
801}