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