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::{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
133impl<const B: Word> Repr<B> {
134    /// The base of the representation. It's exposed as an [IBig] constant.
135    pub const BASE: UBig = UBig::from_word(B);
136
137    /// Create a [Repr] instance representing value zero
138    #[inline]
139    pub const fn zero() -> Self {
140        Self {
141            significand: IBig::ZERO,
142            exponent: 0,
143        }
144    }
145    /// Create a [Repr] instance representing value one
146    #[inline]
147    pub const fn one() -> Self {
148        Self {
149            significand: IBig::ONE,
150            exponent: 0,
151        }
152    }
153    /// Create a [Repr] instance representing value negative one
154    #[inline]
155    pub const fn neg_one() -> Self {
156        Self {
157            significand: IBig::NEG_ONE,
158            exponent: 0,
159        }
160    }
161    /// Create a [Repr] instance representing the (positive) infinity
162    #[inline]
163    pub const fn infinity() -> Self {
164        Self {
165            significand: IBig::ZERO,
166            exponent: isize::MAX,
167        }
168    }
169    /// Create a [Repr] instance representing the negative infinity
170    #[inline]
171    pub const fn neg_infinity() -> Self {
172        Self {
173            significand: IBig::ZERO,
174            exponent: isize::MIN,
175        }
176    }
177    /// Create a [Repr] instance representing the negative zero (`-0`)
178    ///
179    /// Negative zero is produced by operations (e.g. `1 / -inf`, `ceil(-0)`, cancellation
180    /// under round-toward-negative) and is distinct from `+0` only in operations that are
181    /// sensitive to the sign of zero (e.g. `1 / -0 = -inf`). It compares equal to `+0`.
182    #[inline]
183    pub const fn neg_zero() -> Self {
184        Self {
185            significand: IBig::ZERO,
186            exponent: -1,
187        }
188    }
189
190    /// Determine if the [Repr] represents positive zero (`+0`)
191    ///
192    /// This returns `true` only for `+0`; use [`Self::is_neg_zero`] to detect `-0`, or check
193    /// `self.significand().is_zero()` to detect either signed zero.
194    ///
195    /// # Examples
196    ///
197    /// ```
198    /// # use dashu_float::Repr;
199    /// assert!(Repr::<2>::zero().is_pos_zero());
200    /// assert!(!Repr::<10>::neg_zero().is_pos_zero());
201    /// assert!(!Repr::<10>::one().is_pos_zero());
202    /// ```
203    #[inline]
204    pub const fn is_pos_zero(&self) -> bool {
205        self.significand.is_zero() && self.exponent == 0
206    }
207
208    /// Determine if the [Repr] represents the negative zero (`-0`)
209    ///
210    /// # Examples
211    ///
212    /// ```
213    /// # use dashu_float::Repr;
214    /// assert!(Repr::<2>::neg_zero().is_neg_zero());
215    /// assert!(!Repr::<10>::zero().is_neg_zero());
216    /// assert!(!Repr::<10>::one().is_neg_zero());
217    /// ```
218    #[inline]
219    pub const fn is_neg_zero(&self) -> bool {
220        self.significand.is_zero() && self.exponent == -1
221    }
222
223    /// Determine if the [Repr] represents one
224    ///
225    /// # Examples
226    ///
227    /// ```
228    /// # use dashu_float::Repr;
229    /// assert!(Repr::<2>::zero().is_pos_zero());
230    /// assert!(!Repr::<10>::one().is_pos_zero());
231    /// ```
232    #[inline]
233    pub const fn is_one(&self) -> bool {
234        self.significand.is_one() && self.exponent == 0
235    }
236
237    /// Determine if the [Repr] represents the (±)infinity
238    ///
239    /// # Examples
240    ///
241    /// ```
242    /// # use dashu_float::Repr;
243    /// assert!(Repr::<2>::infinity().is_infinite());
244    /// assert!(Repr::<10>::neg_infinity().is_infinite());
245    /// assert!(!Repr::<10>::one().is_infinite());
246    /// assert!(!Repr::<10>::neg_zero().is_infinite());
247    /// ```
248    #[inline]
249    pub const fn is_infinite(&self) -> bool {
250        self.significand.is_zero() && (self.exponent == isize::MAX || self.exponent == isize::MIN)
251    }
252
253    /// Determine if the [Repr] represents a finite number
254    ///
255    /// # Examples
256    ///
257    /// ```
258    /// # use dashu_float::Repr;
259    /// assert!(Repr::<2>::zero().is_finite());
260    /// assert!(Repr::<10>::one().is_finite());
261    /// assert!(!Repr::<16>::infinity().is_finite());
262    /// ```
263    #[inline]
264    pub const fn is_finite(&self) -> bool {
265        !self.is_infinite()
266    }
267
268    /// Determine if the number can be regarded as an integer.
269    ///
270    /// Note that this function returns false when the number is infinite.
271    ///
272    /// # Examples
273    ///
274    /// ```
275    /// # use dashu_float::Repr;
276    /// assert!(Repr::<2>::zero().is_int());
277    /// assert!(Repr::<10>::one().is_int());
278    /// assert!(!Repr::<16>::new(123.into(), -1).is_int());
279    /// ```
280    pub fn is_int(&self) -> bool {
281        if self.is_infinite() {
282            false
283        } else {
284            self.exponent >= 0
285        }
286    }
287
288    /// Get the sign of the number
289    ///
290    /// Note that `-0` has a negative sign (so `1 / -0 = -inf`), while `+0` has a positive sign.
291    ///
292    /// # Examples
293    ///
294    /// ```
295    /// # use dashu_base::Sign;
296    /// # use dashu_float::Repr;
297    /// assert_eq!(Repr::<2>::zero().sign(), Sign::Positive);
298    /// assert_eq!(Repr::<2>::neg_zero().sign(), Sign::Negative);
299    /// assert_eq!(Repr::<2>::neg_one().sign(), Sign::Negative);
300    /// assert_eq!(Repr::<10>::neg_infinity().sign(), Sign::Negative);
301    /// ```
302    #[inline]
303    pub const fn sign(&self) -> Sign {
304        if self.significand.is_zero() {
305            if self.exponent >= 0 {
306                Sign::Positive
307            } else {
308                Sign::Negative
309            }
310        } else {
311            self.significand.sign()
312        }
313    }
314
315    /// Negate the number, correctly toggling the sign of `±0` and `±inf` by flipping the
316    /// special-value exponent (negating the significand alone is a no-op for zero).
317    #[inline]
318    pub(crate) fn neg(self) -> Self {
319        if self.significand.is_zero() {
320            Self {
321                significand: self.significand,
322                exponent: negate_special_exponent(self.exponent),
323            }
324        } else {
325            Self {
326                significand: -self.significand,
327                exponent: self.exponent,
328            }
329        }
330    }
331
332    /// Check that a `Repr` with a non-zero significand has a valid finite exponent.
333    ///
334    /// Returns [`FpError::Overflow`] or [`FpError::Underflow`] when the exponent collides with
335    /// the `+inf`/`-inf` sentinels (`isize::MAX` / `isize::MIN`). Zero-significand reprs
336    /// (canonical special values) always pass.
337    pub(crate) fn check_finite_exponent(self) -> Result<Self, FpError> {
338        if !self.significand.is_zero() {
339            if self.exponent == isize::MAX {
340                Err(FpError::Overflow(self.sign()))
341            } else if self.exponent == isize::MIN {
342                Err(FpError::Underflow(self.sign()))
343            } else {
344                Ok(self)
345            }
346        } else {
347            Ok(self)
348        }
349    }
350
351    /// Create the `Repr` for a signed infinity from the mathematical sign of a result that
352    /// overflowed.
353    #[inline]
354    pub(crate) const fn infinity_with_sign(sign: Sign) -> Self {
355        match sign {
356            Sign::Positive => Self::infinity(),
357            Sign::Negative => Self::neg_infinity(),
358        }
359    }
360
361    /// Create the `Repr` for a signed zero from the mathematical sign of a result that
362    /// underflowed.
363    #[inline]
364    pub(crate) const fn zero_with_sign(sign: Sign) -> Self {
365        match sign {
366            Sign::Positive => Self::zero(),
367            Sign::Negative => Self::neg_zero(),
368        }
369    }
370
371    /// Normalize the float representation so that the significand is not divisible by the base.
372    ///
373    /// A zero significand denotes a canonical special value (`+0`, `-0`, `+inf`, `-inf`) and is
374    /// returned unchanged; any other (non-canonical) zero significand is normalized to `+0`.
375    pub(crate) fn normalize(self) -> Self {
376        if self.significand.is_zero() {
377            // Preserve the four canonical special-value encodings; collapse anything else to +0.
378            if self.exponent == 0
379                || self.exponent == -1
380                || self.exponent == isize::MAX
381                || self.exponent == isize::MIN
382            {
383                return self;
384            }
385            return Self::zero();
386        }
387
388        let Self {
389            mut significand,
390            mut exponent,
391        } = self;
392        if B == 2 {
393            let shift = significand.trailing_zeros().unwrap();
394            significand >>= shift;
395            exponent = exponent.saturating_add(shift as isize);
396        } else if B.is_power_of_two() {
397            let bits = B.trailing_zeros() as usize;
398            let shift = significand.trailing_zeros().unwrap() / bits;
399            significand >>= shift * bits;
400            exponent = exponent.saturating_add(shift as isize);
401        } else {
402            let (sign, mut mag) = significand.into_parts();
403            let shift = mag.remove(&UBig::from_word(B)).unwrap();
404            exponent = exponent.saturating_add(shift as isize);
405            significand = IBig::from_parts(sign, mag);
406        }
407        Self {
408            significand,
409            exponent,
410        }
411    }
412
413    /// Get the number of digits (under base `B`) in the significand.
414    ///
415    /// If the number is 0, then 0 is returned (instead of 1).
416    ///
417    /// # Examples
418    ///
419    /// ```
420    /// # use dashu_float::Repr;
421    /// assert_eq!(Repr::<2>::zero().digits(), 0);
422    /// assert_eq!(Repr::<2>::one().digits(), 1);
423    /// assert_eq!(Repr::<10>::one().digits(), 1);
424    ///
425    /// assert_eq!(Repr::<10>::new(100.into(), 0).digits(), 1); // 1e2
426    /// assert_eq!(Repr::<10>::new(101.into(), 0).digits(), 3);
427    /// ```
428    #[inline]
429    pub fn digits(&self) -> usize {
430        assert_finite(self);
431        digit_len::<B>(&self.significand)
432    }
433
434    /// Fast over-estimation of [digits][Self::digits]
435    ///
436    /// # Examples
437    ///
438    /// ```
439    /// # use dashu_float::Repr;
440    /// assert_eq!(Repr::<2>::zero().digits_ub(), 0);
441    /// assert_eq!(Repr::<2>::one().digits_ub(), 1);
442    /// assert_eq!(Repr::<10>::one().digits_ub(), 1);
443    /// assert_eq!(Repr::<2>::new(31.into(), 0).digits_ub(), 5);
444    /// assert_eq!(Repr::<10>::new(99.into(), 0).digits_ub(), 2);
445    /// ```
446    #[inline]
447    pub fn digits_ub(&self) -> usize {
448        assert_finite(self);
449        if self.significand.is_zero() {
450            return 0;
451        }
452
453        let log = match B {
454            2 => self.significand.log2_bounds().1,
455            10 => self.significand.log2_bounds().1 * core::f32::consts::LOG10_2,
456            _ => self.significand.log2_bounds().1 / Self::BASE.log2_bounds().0,
457        };
458        log as usize + 1
459    }
460
461    /// Fast under-estimation of [digits][Self::digits]
462    ///
463    /// # Examples
464    ///
465    /// ```
466    /// # use dashu_float::Repr;
467    /// assert_eq!(Repr::<2>::zero().digits_lb(), 0);
468    /// assert_eq!(Repr::<2>::one().digits_lb(), 0);
469    /// assert_eq!(Repr::<10>::one().digits_lb(), 0);
470    /// assert!(Repr::<10>::new(1001.into(), 0).digits_lb() <= 3);
471    /// ```
472    #[inline]
473    pub fn digits_lb(&self) -> usize {
474        assert_finite(self);
475        if self.significand.is_zero() {
476            return 0;
477        }
478
479        let log = match B {
480            2 => self.significand.log2_bounds().0,
481            10 => self.significand.log2_bounds().0 * core::f32::consts::LOG10_2,
482            _ => self.significand.log2_bounds().0 / Self::BASE.log2_bounds().1,
483        };
484        log as usize
485    }
486
487    /// Quickly test if `|self| < 1`. IT's not always correct,
488    /// but there are guaranteed to be no false postives.
489    #[inline]
490    pub(crate) fn smaller_than_one(&self) -> bool {
491        debug_assert!(self.is_finite());
492        self.exponent + (self.digits_ub() as isize) < -1
493    }
494
495    /// Create a [Repr] from the significand and exponent. This
496    /// constructor will normalize the representation.
497    ///
498    /// # Examples
499    ///
500    /// ```
501    /// # use dashu_int::IBig;
502    /// # use dashu_float::Repr;
503    /// let a = Repr::<2>::new(400.into(), -2);
504    /// assert_eq!(a.significand(), &IBig::from(25));
505    /// assert_eq!(a.exponent(), 2);
506    ///
507    /// let b = Repr::<10>::new(400.into(), -2);
508    /// assert_eq!(b.significand(), &IBig::from(4));
509    /// assert_eq!(b.exponent(), 0);
510    /// ```
511    #[inline]
512    pub fn new(significand: IBig, exponent: isize) -> Self {
513        Self {
514            significand,
515            exponent,
516        }
517        .normalize()
518    }
519
520    /// Get the significand of the representation
521    #[inline]
522    pub fn significand(&self) -> &IBig {
523        &self.significand
524    }
525
526    /// Get the exponent of the representation
527    #[inline]
528    pub fn exponent(&self) -> isize {
529        self.exponent
530    }
531
532    /// Convert the float number into raw `(signficand, exponent)` parts
533    ///
534    /// # Examples
535    ///
536    /// ```
537    /// # use dashu_float::Repr;
538    /// use dashu_int::IBig;
539    ///
540    /// let a = Repr::<2>::new(400.into(), -2);
541    /// assert_eq!(a.into_parts(), (IBig::from(25), 2));
542    ///
543    /// let b = Repr::<10>::new(400.into(), -2);
544    /// assert_eq!(b.into_parts(), (IBig::from(4), 0));
545    /// ```
546    #[inline]
547    pub fn into_parts(self) -> (IBig, isize) {
548        (self.significand, self.exponent)
549    }
550
551    /// Create an Repr from a static sequence of [Word][crate::Word]s representing the significand.
552    ///
553    /// This method is intended for static creation macros.
554    #[doc(hidden)]
555    #[rustversion::since(1.64)]
556    #[inline]
557    pub const unsafe fn from_static_words(
558        sign: Sign,
559        significand: &'static [Word],
560        exponent: isize,
561    ) -> Self {
562        let significand = IBig::from_static_words(sign, significand);
563        assert!(!significand.is_multiple_of_const(B as _));
564
565        Self {
566            significand,
567            exponent,
568        }
569    }
570}
571
572// This custom implementation is necessary due to https://github.com/rust-lang/rust/issues/98374
573impl<const B: Word> Clone for Repr<B> {
574    #[inline]
575    fn clone(&self) -> Self {
576        Self {
577            significand: self.significand.clone(),
578            exponent: self.exponent,
579        }
580    }
581
582    #[inline]
583    fn clone_from(&mut self, source: &Self) {
584        self.significand.clone_from(&source.significand);
585        self.exponent = source.exponent;
586    }
587}
588
589impl<R: Round> Context<R> {
590    /// Create a float operation context with the given precision limit.
591    #[inline]
592    pub const fn new(precision: usize) -> Self {
593        Self {
594            precision,
595            _marker: PhantomData,
596        }
597    }
598
599    /// Create a float operation context with the higher precision from the two context inputs.
600    ///
601    /// # Examples
602    ///
603    /// ```
604    /// use dashu_float::{Context, round::mode::Zero};
605    ///
606    /// let ctxt1 = Context::<Zero>::new(2);
607    /// let ctxt2 = Context::<Zero>::new(5);
608    /// assert_eq!(Context::max(ctxt1, ctxt2).precision(), 5);
609    /// ```
610    #[inline]
611    pub const fn max(lhs: Self, rhs: Self) -> Self {
612        Self {
613            // this comparison also correctly handles ulimited precisions (precision = 0)
614            precision: if lhs.precision > rhs.precision {
615                lhs.precision
616            } else {
617                rhs.precision
618            },
619            _marker: PhantomData,
620        }
621    }
622
623    /// Check whether the precision is limited (not zero)
624    #[inline]
625    pub(crate) const fn is_limited(&self) -> bool {
626        self.precision != 0
627    }
628
629    /// Get the precision limited from the context
630    #[inline]
631    pub const fn precision(&self) -> usize {
632        self.precision
633    }
634
635    /// Round the repr to the desired precision
636    pub(crate) fn repr_round<const B: Word>(&self, repr: Repr<B>) -> Rounded<Repr<B>> {
637        assert_finite(&repr);
638        if !self.is_limited() {
639            return Exact(repr);
640        }
641
642        let digits = repr.digits();
643        if digits > self.precision {
644            let shift = digits - self.precision;
645            let input_neg = repr.sign() == Sign::Negative;
646            let (signif_hi, signif_lo) = split_digits::<B>(repr.significand, shift);
647            let adjust = R::round_fract::<B>(&signif_hi, signif_lo, shift);
648            let sig = signif_hi + adjust;
649            let result = rounded_to_repr(sig, repr.exponent + shift as isize, input_neg);
650            Inexact(result, adjust)
651        } else {
652            Exact(repr)
653        }
654    }
655
656    /// Round the repr to the desired precision
657    pub(crate) fn repr_round_ref<const B: Word>(&self, repr: &Repr<B>) -> Rounded<Repr<B>> {
658        assert_finite(repr);
659        if !self.is_limited() {
660            return Exact(repr.clone());
661        }
662
663        let digits = repr.digits();
664        if digits > self.precision {
665            let shift = digits - self.precision;
666            let input_neg = repr.sign() == Sign::Negative;
667            let (signif_hi, signif_lo) = split_digits_ref::<B>(&repr.significand, shift);
668            let adjust = R::round_fract::<B>(&signif_hi, signif_lo, shift);
669            let sig = signif_hi + adjust;
670            let result = rounded_to_repr(sig, repr.exponent + shift as isize, input_neg);
671            Inexact(result, adjust)
672        } else {
673            Exact(repr.clone())
674        }
675    }
676}
677
678#[cfg(test)]
679mod tests {
680    use super::*;
681    use dashu_base::Sign;
682
683    #[test]
684    fn infinity_encoding() {
685        assert_eq!(Repr::<2>::infinity().exponent, isize::MAX);
686        assert_eq!(Repr::<10>::neg_infinity().exponent, isize::MIN);
687        assert!(Repr::<2>::infinity().is_infinite());
688        assert!(Repr::<10>::neg_infinity().is_infinite());
689        assert!(!Repr::<2>::infinity().is_finite());
690        assert_eq!(Repr::<2>::infinity().sign(), Sign::Positive);
691        assert_eq!(Repr::<10>::neg_infinity().sign(), Sign::Negative);
692    }
693
694    #[test]
695    fn neg_zero_encoding() {
696        assert_eq!(Repr::<2>::neg_zero().exponent, -1);
697        assert!(Repr::<2>::neg_zero().is_neg_zero());
698        assert!(!Repr::<2>::neg_zero().is_pos_zero());
699        assert!(!Repr::<2>::neg_zero().is_infinite());
700        assert_eq!(Repr::<2>::neg_zero().sign(), Sign::Negative);
701        assert_eq!(Repr::<2>::zero().sign(), Sign::Positive);
702    }
703
704    #[test]
705    fn normalize_preserves_specials() {
706        // infinities are preserved (the previous clobbering bug)
707        assert_eq!(Repr::<2>::infinity(), Repr::<2>::infinity().normalize());
708        assert_eq!(Repr::<10>::neg_infinity(), Repr::<10>::neg_infinity().normalize());
709        // +0 is preserved
710        assert_eq!(Repr::<2>::zero(), Repr::<2>::zero().normalize());
711        // a stray zero significand with a non-sentinel exponent collapses to +0
712        let stray: Repr<2> = Repr {
713            significand: IBig::ZERO,
714            exponent: 7,
715        };
716        assert_eq!(Repr::<2>::zero(), stray.normalize());
717        // non-zero significands are still normalized
718        let r: Repr<2> = Repr {
719            significand: IBig::from(0b10100i32),
720            exponent: 0,
721        };
722        let r = r.normalize();
723        assert_eq!(r.significand, IBig::from(0b101i32));
724        assert_eq!(r.exponent, 2);
725    }
726}