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