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}