Skip to main content

dashu_float/
repr.rs

1use crate::{
2    error::assert_finite,
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
25///
26/// This struct supports representing the infinity, but the infinity is only supposed to be used
27/// as sentinels. That is, only equality test and comparison are implemented for the infinity.
28/// Any other operations on the infinity will lead to panic. If an operation result is too large
29/// or too small, the operation will **panic** instead of returning an infinity.
30///
31#[derive(PartialEq, Eq)]
32pub struct Repr<const BASE: Word> {
33    /// The significand of the floating point number. If the significand is zero, then the number is:
34    /// - Zero, if exponent = 0
35    /// - Positive infinity, if exponent > 0
36    /// - Negative infinity, if exponent < 0
37    pub(crate) significand: IBig,
38
39    /// The exponent of the floating point number.
40    pub(crate) exponent: isize,
41}
42
43/// The context containing runtime information for the floating point number and its operations.
44///
45/// The context currently consists of a *precision limit* and a *rounding mode*. All the operation
46/// associated with the context will be precise to the **full precision** (`|error| < 1 ulp`).
47/// The rounding result returned from the functions tells additional error information, see
48/// [the rounding mode module][crate::round::mode] for details.
49///
50/// # Precision
51///
52/// The precision limit determine the number of significant digits in the float number.
53///
54/// For binary operations, the result will have the higher one between the precisions of two
55/// operands.
56///
57/// If the precision is set to 0, then the precision is **unlimited** during operations.
58/// Be cautious to use unlimited precision because it can leads to very huge significands.
59/// Unlimited precision is forbidden for some operations where the result is always inexact.
60///
61/// # Rounding Mode
62///
63/// The rounding mode determines the rounding behavior of the float operations.
64///
65/// See [the rounding mode module][crate::round::mode] for built-in rounding modes.
66/// Users can implement custom rounding mode by implementing the [Round][crate::round::Round]
67/// trait, but this is discouraged since in the future we might restrict the rounding
68/// modes to be chosen from the the built-in modes.
69///
70/// For binary operations, the two oprands must have the same rounding mode.
71///
72#[derive(Clone, Copy)]
73pub struct Context<RoundingMode: Round> {
74    /// The precision of the floating point number.
75    /// If set to zero, then the precision is unlimited.
76    pub(crate) precision: usize,
77    _marker: PhantomData<RoundingMode>,
78}
79
80impl<const B: Word> Repr<B> {
81    /// The base of the representation. It's exposed as an [IBig] constant.
82    pub const BASE: UBig = UBig::from_word(B);
83
84    /// Create a [Repr] instance representing value zero
85    #[inline]
86    pub const fn zero() -> Self {
87        Self {
88            significand: IBig::ZERO,
89            exponent: 0,
90        }
91    }
92    /// Create a [Repr] instance representing value one
93    #[inline]
94    pub const fn one() -> Self {
95        Self {
96            significand: IBig::ONE,
97            exponent: 0,
98        }
99    }
100    /// Create a [Repr] instance representing value negative one
101    #[inline]
102    pub const fn neg_one() -> Self {
103        Self {
104            significand: IBig::NEG_ONE,
105            exponent: 0,
106        }
107    }
108    /// Create a [Repr] instance representing the (positive) infinity
109    #[inline]
110    pub const fn infinity() -> Self {
111        Self {
112            significand: IBig::ZERO,
113            exponent: 1,
114        }
115    }
116    /// Create a [Repr] instance representing the negative infinity
117    #[inline]
118    pub const fn neg_infinity() -> Self {
119        Self {
120            significand: IBig::ZERO,
121            exponent: -1,
122        }
123    }
124
125    // XXX: Add support for representing NEG_ZERO, but don't provide method to generate it.
126    // neg_zero: exponent -1, infinity: exponent: isize::MAX, neg_infinity: exponent: isize::MIN
127
128    /// Determine if the [Repr] represents zero
129    ///
130    /// # Examples
131    ///
132    /// ```
133    /// # use dashu_float::Repr;
134    /// assert!(Repr::<2>::zero().is_zero());
135    /// assert!(!Repr::<10>::one().is_zero());
136    /// ```
137    #[inline]
138    pub const fn is_zero(&self) -> bool {
139        self.significand.is_zero() && self.exponent == 0
140    }
141
142    /// Determine if the [Repr] represents one
143    ///
144    /// # Examples
145    ///
146    /// ```
147    /// # use dashu_float::Repr;
148    /// assert!(Repr::<2>::zero().is_zero());
149    /// assert!(!Repr::<10>::one().is_zero());
150    /// ```
151    #[inline]
152    pub const fn is_one(&self) -> bool {
153        self.significand.is_one() && self.exponent == 0
154    }
155
156    /// Determine if the [Repr] represents the (±)infinity
157    ///
158    /// # Examples
159    ///
160    /// ```
161    /// # use dashu_float::Repr;
162    /// assert!(Repr::<2>::infinity().is_infinite());
163    /// assert!(Repr::<10>::neg_infinity().is_infinite());
164    /// assert!(!Repr::<10>::one().is_infinite());
165    /// ```
166    #[inline]
167    pub const fn is_infinite(&self) -> bool {
168        self.significand.is_zero() && self.exponent != 0
169    }
170
171    /// Determine if the [Repr] represents a finite number
172    ///
173    /// # Examples
174    ///
175    /// ```
176    /// # use dashu_float::Repr;
177    /// assert!(Repr::<2>::zero().is_finite());
178    /// assert!(Repr::<10>::one().is_finite());
179    /// assert!(!Repr::<16>::infinity().is_finite());
180    /// ```
181    #[inline]
182    pub const fn is_finite(&self) -> bool {
183        !self.is_infinite()
184    }
185
186    /// Determine if the number can be regarded as an integer.
187    ///
188    /// Note that this function returns false when the number is infinite.
189    ///
190    /// # Examples
191    ///
192    /// ```
193    /// # use dashu_float::Repr;
194    /// assert!(Repr::<2>::zero().is_int());
195    /// assert!(Repr::<10>::one().is_int());
196    /// assert!(!Repr::<16>::new(123.into(), -1).is_int());
197    /// ```
198    pub fn is_int(&self) -> bool {
199        if self.is_infinite() {
200            false
201        } else {
202            self.exponent >= 0
203        }
204    }
205
206    /// Get the sign of the number
207    ///
208    /// # Examples
209    ///
210    /// ```
211    /// # use dashu_base::Sign;
212    /// # use dashu_float::Repr;
213    /// assert_eq!(Repr::<2>::zero().sign(), Sign::Positive);
214    /// assert_eq!(Repr::<2>::neg_one().sign(), Sign::Negative);
215    /// assert_eq!(Repr::<10>::neg_infinity().sign(), Sign::Negative);
216    /// ```
217    #[inline]
218    pub const fn sign(&self) -> Sign {
219        if self.significand.is_zero() {
220            if self.exponent >= 0 {
221                Sign::Positive
222            } else {
223                Sign::Negative
224            }
225        } else {
226            self.significand.sign()
227        }
228    }
229
230    /// Normalize the float representation so that the significand is not divisible by the base.
231    /// Any floats with zero significand will be considered as zero value (instead of an `INFINITY`)
232    pub(crate) fn normalize(self) -> Self {
233        let Self {
234            mut significand,
235            mut exponent,
236        } = self;
237        if significand.is_zero() {
238            return Self::zero();
239        }
240
241        if B == 2 {
242            let shift = significand.trailing_zeros().unwrap();
243            significand >>= shift;
244            exponent += shift as isize;
245        } else if B.is_power_of_two() {
246            let bits = B.trailing_zeros() as usize;
247            let shift = significand.trailing_zeros().unwrap() / bits;
248            significand >>= shift * bits;
249            exponent += shift as isize;
250        } else {
251            let (sign, mut mag) = significand.into_parts();
252            let shift = mag.remove(&UBig::from_word(B)).unwrap();
253            exponent += shift as isize;
254            significand = IBig::from_parts(sign, mag);
255        }
256        Self {
257            significand,
258            exponent,
259        }
260    }
261
262    /// Get the number of digits (under base `B`) in the significand.
263    ///
264    /// If the number is 0, then 0 is returned (instead of 1).
265    ///
266    /// # Examples
267    ///
268    /// ```
269    /// # use dashu_float::Repr;
270    /// assert_eq!(Repr::<2>::zero().digits(), 0);
271    /// assert_eq!(Repr::<2>::one().digits(), 1);
272    /// assert_eq!(Repr::<10>::one().digits(), 1);
273    ///
274    /// assert_eq!(Repr::<10>::new(100.into(), 0).digits(), 1); // 1e2
275    /// assert_eq!(Repr::<10>::new(101.into(), 0).digits(), 3);
276    /// ```
277    #[inline]
278    pub fn digits(&self) -> usize {
279        assert_finite(self);
280        digit_len::<B>(&self.significand)
281    }
282
283    /// Fast over-estimation of [digits][Self::digits]
284    ///
285    /// # Examples
286    ///
287    /// ```
288    /// # use dashu_float::Repr;
289    /// assert_eq!(Repr::<2>::zero().digits_ub(), 0);
290    /// assert_eq!(Repr::<2>::one().digits_ub(), 1);
291    /// assert_eq!(Repr::<10>::one().digits_ub(), 1);
292    /// assert_eq!(Repr::<2>::new(31.into(), 0).digits_ub(), 5);
293    /// assert_eq!(Repr::<10>::new(99.into(), 0).digits_ub(), 2);
294    /// ```
295    #[inline]
296    pub fn digits_ub(&self) -> usize {
297        assert_finite(self);
298        if self.significand.is_zero() {
299            return 0;
300        }
301
302        let log = match B {
303            2 => self.significand.log2_bounds().1,
304            10 => self.significand.log2_bounds().1 * core::f32::consts::LOG10_2,
305            _ => self.significand.log2_bounds().1 / Self::BASE.log2_bounds().0,
306        };
307        log as usize + 1
308    }
309
310    /// Fast under-estimation of [digits][Self::digits]
311    ///
312    /// # Examples
313    ///
314    /// ```
315    /// # use dashu_float::Repr;
316    /// assert_eq!(Repr::<2>::zero().digits_lb(), 0);
317    /// assert_eq!(Repr::<2>::one().digits_lb(), 0);
318    /// assert_eq!(Repr::<10>::one().digits_lb(), 0);
319    /// assert!(Repr::<10>::new(1001.into(), 0).digits_lb() <= 3);
320    /// ```
321    #[inline]
322    pub fn digits_lb(&self) -> usize {
323        assert_finite(self);
324        if self.significand.is_zero() {
325            return 0;
326        }
327
328        let log = match B {
329            2 => self.significand.log2_bounds().0,
330            10 => self.significand.log2_bounds().0 * core::f32::consts::LOG10_2,
331            _ => self.significand.log2_bounds().0 / Self::BASE.log2_bounds().1,
332        };
333        log as usize
334    }
335
336    /// Quickly test if `|self| < 1`. IT's not always correct,
337    /// but there are guaranteed to be no false postives.
338    #[inline]
339    pub(crate) fn smaller_than_one(&self) -> bool {
340        debug_assert!(self.is_finite());
341        self.exponent + (self.digits_ub() as isize) < -1
342    }
343
344    /// Create a [Repr] from the significand and exponent. This
345    /// constructor will normalize the representation.
346    ///
347    /// # Examples
348    ///
349    /// ```
350    /// # use dashu_int::IBig;
351    /// # use dashu_float::Repr;
352    /// let a = Repr::<2>::new(400.into(), -2);
353    /// assert_eq!(a.significand(), &IBig::from(25));
354    /// assert_eq!(a.exponent(), 2);
355    ///
356    /// let b = Repr::<10>::new(400.into(), -2);
357    /// assert_eq!(b.significand(), &IBig::from(4));
358    /// assert_eq!(b.exponent(), 0);
359    /// ```
360    #[inline]
361    pub fn new(significand: IBig, exponent: isize) -> Self {
362        Self {
363            significand,
364            exponent,
365        }
366        .normalize()
367    }
368
369    /// Get the significand of the representation
370    #[inline]
371    pub fn significand(&self) -> &IBig {
372        &self.significand
373    }
374
375    /// Get the exponent of the representation
376    #[inline]
377    pub fn exponent(&self) -> isize {
378        self.exponent
379    }
380
381    /// Convert the float number into raw `(signficand, exponent)` parts
382    ///
383    /// # Examples
384    ///
385    /// ```
386    /// # use dashu_float::Repr;
387    /// use dashu_int::IBig;
388    ///
389    /// let a = Repr::<2>::new(400.into(), -2);
390    /// assert_eq!(a.into_parts(), (IBig::from(25), 2));
391    ///
392    /// let b = Repr::<10>::new(400.into(), -2);
393    /// assert_eq!(b.into_parts(), (IBig::from(4), 0));
394    /// ```
395    #[inline]
396    pub fn into_parts(self) -> (IBig, isize) {
397        (self.significand, self.exponent)
398    }
399
400    /// Create an Repr from a static sequence of [Word][crate::Word]s representing the significand.
401    ///
402    /// This method is intended for static creation macros.
403    #[doc(hidden)]
404    #[rustversion::since(1.64)]
405    #[inline]
406    pub const unsafe fn from_static_words(
407        sign: Sign,
408        significand: &'static [Word],
409        exponent: isize,
410    ) -> Self {
411        let significand = IBig::from_static_words(sign, significand);
412        assert!(!significand.is_multiple_of_const(B as _));
413
414        Self {
415            significand,
416            exponent,
417        }
418    }
419}
420
421// This custom implementation is necessary due to https://github.com/rust-lang/rust/issues/98374
422impl<const B: Word> Clone for Repr<B> {
423    #[inline]
424    fn clone(&self) -> Self {
425        Self {
426            significand: self.significand.clone(),
427            exponent: self.exponent,
428        }
429    }
430
431    #[inline]
432    fn clone_from(&mut self, source: &Self) {
433        self.significand.clone_from(&source.significand);
434        self.exponent = source.exponent;
435    }
436}
437
438impl<R: Round> Context<R> {
439    /// Create a float operation context with the given precision limit.
440    #[inline]
441    pub const fn new(precision: usize) -> Self {
442        Self {
443            precision,
444            _marker: PhantomData,
445        }
446    }
447
448    /// Create a float operation context with the higher precision from the two context inputs.
449    ///
450    /// # Examples
451    ///
452    /// ```
453    /// use dashu_float::{Context, round::mode::Zero};
454    ///
455    /// let ctxt1 = Context::<Zero>::new(2);
456    /// let ctxt2 = Context::<Zero>::new(5);
457    /// assert_eq!(Context::max(ctxt1, ctxt2).precision(), 5);
458    /// ```
459    #[inline]
460    pub const fn max(lhs: Self, rhs: Self) -> Self {
461        Self {
462            // this comparison also correctly handles ulimited precisions (precision = 0)
463            precision: if lhs.precision > rhs.precision {
464                lhs.precision
465            } else {
466                rhs.precision
467            },
468            _marker: PhantomData,
469        }
470    }
471
472    /// Check whether the precision is limited (not zero)
473    #[inline]
474    pub(crate) const fn is_limited(&self) -> bool {
475        self.precision != 0
476    }
477
478    /// Get the precision limited from the context
479    #[inline]
480    pub const fn precision(&self) -> usize {
481        self.precision
482    }
483
484    /// Round the repr to the desired precision
485    pub(crate) fn repr_round<const B: Word>(&self, repr: Repr<B>) -> Rounded<Repr<B>> {
486        assert_finite(&repr);
487        if !self.is_limited() {
488            return Exact(repr);
489        }
490
491        let digits = repr.digits();
492        if digits > self.precision {
493            let shift = digits - self.precision;
494            let (signif_hi, signif_lo) = split_digits::<B>(repr.significand, shift);
495            let adjust = R::round_fract::<B>(&signif_hi, signif_lo, shift);
496            Inexact(Repr::new(signif_hi + adjust, repr.exponent + shift as isize), adjust)
497        } else {
498            Exact(repr)
499        }
500    }
501
502    /// Round the repr to the desired precision
503    pub(crate) fn repr_round_ref<const B: Word>(&self, repr: &Repr<B>) -> Rounded<Repr<B>> {
504        assert_finite(repr);
505        if !self.is_limited() {
506            return Exact(repr.clone());
507        }
508
509        let digits = repr.digits();
510        if digits > self.precision {
511            let shift = digits - self.precision;
512            let (signif_hi, signif_lo) = split_digits_ref::<B>(&repr.significand, shift);
513            let adjust = R::round_fract::<B>(&signif_hi, signif_lo, shift);
514            Inexact(Repr::new(signif_hi + adjust, repr.exponent + shift as isize), adjust)
515        } else {
516            Exact(repr.clone())
517        }
518    }
519}