dashu_float/
fbig.rs

1use crate::{
2    error::panic_unlimited_precision,
3    repr::{Context, Repr, Word},
4    round::{mode, Round},
5    utils::digit_len,
6};
7use dashu_base::Sign;
8use dashu_int::{DoubleWord, IBig};
9
10/// An arbitrary precision floating point number with arbitrary base and rounding mode.
11///
12/// The float number consists of a [Repr] and a [Context]. The [Repr] instance determines
13/// the value of the number, and the [Context] contains runtime information (such as precision
14/// limit, rounding mode, etc.)
15///
16/// For how the number is represented, see [Repr], for how the precision limit and rounding
17/// mode is applied, see [Context].
18///
19/// The arithmetic operations on [FBig] follows the behavior of its associated context.
20/// If a different precision limit and/or rounding mode is required, or the rounding
21/// information has to be preserved, use the methods of the [Context] type.
22///
23/// # Generic Parameters
24///
25/// The const generic parameters will be abbreviated as `BASE` -> `B`, `RoundingMode` -> `R`.
26/// THe `BASE` must be in range \[2, isize::MAX\], and the `RoundingMode` can be chosen from
27/// the [mode] module.
28///
29/// With the default generic parameters, the floating number is of base 2 rounded towards zero.
30/// This is the most efficient format for operations. To represent a decimal number, the alias
31/// [DBig][crate::DBig] is provided, which is base 10 rounded to the nearest.
32///
33/// # Parsing and printing
34///
35/// To create a [FBig] instance, there are four ways:
36/// 1. Use predifined constants (e.g. [FBig::ZERO], [FBig::ONE], [FBig::NEG_INFINITY]).
37/// 1. Use the literal macro `fbig!` or `dbig!` defined in the [`dashu-macro`](https://docs.rs/dashu-macros/latest/dashu_macros/) crate.
38/// 1. Construct from the significand and exponent using [from_parts()][FBig::from_parts] or [from_parts_const()][FBig::from_parts_const].
39/// 1. Parse from a string.
40///
41/// Conversion from and to [str] is limited to native radix (i.e. base). To print or parse
42/// with different radix, please use [to_binary()][FBig::to_binary], [to_decimal()][FBig::to_decimal]
43/// or [with_base()][FBig::with_base], [with_base_and_precision()][FBig::with_base_and_precision] to convert.
44///
45/// For printing, currently only the [Display][core::fmt::Display] and [Debug][core::fmt::Debug] are supported.
46/// Other formatting traits will be supported in future.
47///
48/// ```
49/// # use dashu_base::ParseError;
50/// # use dashu_float::DBig;
51/// use core::str::FromStr;
52///
53/// // parsing
54/// let a = DBig::from_parts(123456789.into(), -5);
55/// let b = DBig::from_str("1234.56789")?;
56/// let c = DBig::from_str("1.23456789e3")?;
57/// assert_eq!(a, b);
58/// assert_eq!(b, c);
59///
60/// // printing
61/// assert_eq!(format!("{}", DBig::from_str("12.34")?), "12.34");
62/// let x = DBig::from_str("10.01")?
63///     .with_precision(0) // use unlimited precision
64///     .value();
65/// if dashu_int::Word::BITS == 64 {
66///     // number of digits to display depends on the word size
67///     assert_eq!(
68///         format!("{:?}", x.powi(100.into())),
69///         "1105115697720767968..1441386704950100001 * 10 ^ -200 (prec: 0)"
70///     );
71/// }
72/// # Ok::<(), ParseError>(())
73/// ```
74///
75/// For detailed information of parsing, refer to the [from_str_native()][FBig::from_str_native] method.
76///
77/// # Restriction on binary operators
78///
79/// Binary operators on [FBig] instances are restricted to the same base and same rounding mode. This is
80/// designed to make sure that no hidden conversion is performed during the operators. However, for equality
81/// test and comparsion, two [FBig] instances can have different rounding modes (but not different bases),
82/// because rounding will never happends during comparison.
83///
84/// The infinities are converted as it is, and the subnormals are converted using its actual values.
85///
86/// # IEEE 754 behavior compliance
87///
88/// The representation of the floating point number doesn't follows the IEEE 754 standard, as it's not
89/// designed for arbitrary precision numbers. The key differences include:
90/// * [FBig] doesn't support NaN values. In places where IEEE 754 operations generate NaNs, `FBig` will panic.
91/// * [FBig] doesn't have subnormal values.
92/// * [FBig] doesn't have negative zeros¹. There is only on zero value ([FBig::ZERO]).
93/// * Division by zero and logarithm on zero panic instead of returning infinities.
94/// * [FBig] operations will panic if the result overflows or underflows¹.
95/// * [FBig] does support infinities, but currently infinities are not allowed to be operated with, except for
96///   equality test and comparison¹.
97///
98/// ¹ These behaviors are subject to changes in the future.
99///
100/// # Convert from/to `f32`/`f64`
101///
102/// Converting from [f32]/[f64] to [FBig] is only defined for base 2 [FBig] (using [TryFrom][core::convert::TryFrom])
103/// to ensure the conversion is lossless. Since [FBig] doesn't support `NAN`s, converting `f32::NAN` or `f64::NAN` will
104/// return [Err].
105///
106/// Converting to [f32]/[f64] (using [to_f32()][FBig::to_f32] and [to_f64()][FBig::to_f64]) can be lossy, and the rounding
107/// direction is contained in the result of these two methods. To use the default IEEE 754 rounding mode (rounding to
108/// nearest), the [Repr::to_f32] and [Repr::to_f64] methods can be used for convenience.
109///
110/// # Convert from/to `UBig`/`IBig`
111///
112/// Converting from `UBig` and `IBig` is trivial and lossless through [From]. However, the reverse direction can be lossy.
113///
114/// The [TryFrom] trait and [to_int()][FBig::to_int] method are the two supported ways to convert from [FBig] to [IBig].
115/// To convert to [UBig][dashu_int::UBig], please first convert to [IBig]. When converting to [IBig], [TryFrom] returns
116/// [Ok] only when the floating point number is not infinite and doesn't have fractional part. To convert with rounding,
117/// use [to_int()][FBig::to_int] instead.
118pub struct FBig<RoundingMode: Round = mode::Zero, const BASE: Word = 2> {
119    pub(crate) repr: Repr<BASE>,
120    pub(crate) context: Context<RoundingMode>,
121}
122
123impl<R: Round, const B: Word> FBig<R, B> {
124    /// Create a [FBig] instance from raw parts, internal use only
125    #[inline]
126    pub(crate) const fn new(repr: Repr<B>, context: Context<R>) -> Self {
127        Self { repr, context }
128    }
129
130    /// Create a [FBig] instance from [Repr] and [Context].
131    ///
132    /// This method should not be used in most cases. It's designed to be used when
133    /// you hold a [Repr] instance and want to create an [FBig] from that.
134    ///
135    /// # Examples
136    ///
137    /// ```
138    /// # use dashu_float::DBig;
139    /// use dashu_float::{Repr, Context};
140    ///
141    /// assert_eq!(DBig::from_repr(Repr::one(), Context::new(1)), DBig::ONE);
142    /// assert_eq!(DBig::from_repr(Repr::infinity(), Context::new(1)), DBig::INFINITY);
143    /// ```
144    ///
145    /// # Panics
146    ///
147    /// Panics if the [Repr] has more digits than the precision limit specified in the context.
148    /// Note that this condition is not checked in release builds.
149    #[inline]
150    pub fn from_repr(repr: Repr<B>, context: Context<R>) -> Self {
151        debug_assert!(
152            repr.is_infinite() || !context.is_limited() || repr.digits() <= context.precision
153        );
154        Self { repr, context }
155    }
156
157    /// Create a [FBig] instance from [Repr]. Due to the limitation of const operations,
158    /// the precision of the float is set to unlimited.
159    ///
160    /// # Examples
161    ///
162    /// ```
163    /// # use dashu_float::DBig;
164    /// use dashu_float::{Repr, Context};
165    ///
166    /// assert_eq!(DBig::from_repr_const(Repr::one()), DBig::ONE);
167    /// assert_eq!(DBig::from_repr_const(Repr::infinity()), DBig::INFINITY);
168    /// ```
169    #[inline]
170    pub const fn from_repr_const(repr: Repr<B>) -> Self {
171        Self {
172            repr,
173            context: Context::new(0),
174        }
175    }
176
177    /// [FBig] with value 0 and unlimited precision
178    ///
179    /// To test if the float number is zero, use `self.repr().is_zero()`.
180    pub const ZERO: Self = Self::new(Repr::zero(), Context::new(0));
181
182    /// [FBig] with value 1 and unlimited precision
183    ///
184    /// To test if the float number is one, use `self.repr().is_one()`.
185    pub const ONE: Self = Self::new(Repr::one(), Context::new(0));
186
187    /// [FBig] with value -1 and unlimited precision
188    pub const NEG_ONE: Self = Self::new(Repr::neg_one(), Context::new(0));
189
190    /// [FBig] instance representing the positive infinity (+∞)
191    ///
192    /// To test if the float number is infinite, use `self.repr().infinite()`.
193    pub const INFINITY: Self = Self::new(Repr::infinity(), Context::new(0));
194
195    /// [FBig] instance representing the negative infinity (-∞)
196    ///
197    /// To test if the float number is infinite, use `self.repr().infinite()`.
198    pub const NEG_INFINITY: Self = Self::new(Repr::neg_infinity(), Context::new(0));
199
200    /// Get the maximum precision set for the float number.
201    ///
202    /// It's equivalent to `self.context().precision()`.
203    ///
204    /// # Examples
205    ///
206    /// ```
207    /// # use core::str::FromStr;
208    /// # use dashu_base::ParseError;
209    /// # use dashu_float::DBig;
210    /// # use dashu_int::IBig;
211    /// use dashu_float::Repr;
212    ///
213    /// let a = DBig::from_str("1.234")?;
214    /// assert!(a.repr().significand() <= &IBig::from(10).pow(a.precision()));
215    /// # Ok::<(), ParseError>(())
216    /// ```
217    #[inline]
218    pub const fn precision(&self) -> usize {
219        self.context.precision
220    }
221
222    /// Get the number of the significant digits in the float number
223    ///
224    /// It's equivalent to `self.repr().digits()`.
225    ///
226    /// This value is also the actual precision needed for the float number. Shrink to this
227    /// value using [with_precision()][FBig::with_precision] will not cause loss of float precision.
228    ///
229    /// # Examples
230    ///
231    /// ```
232    /// # use core::str::FromStr;
233    /// # use dashu_base::ParseError;
234    /// # use dashu_float::DBig;
235    /// use dashu_base::Approximation::*;
236    ///
237    /// let a = DBig::from_str("-1.234e-3")?;
238    /// assert_eq!(a.digits(), 4);
239    /// assert!(matches!(a.clone().with_precision(4), Exact(_)));
240    /// assert!(matches!(a.clone().with_precision(3), Inexact(_, _)));
241    /// # Ok::<(), ParseError>(())
242    /// ```
243    #[inline]
244    pub fn digits(&self) -> usize {
245        self.repr.digits()
246    }
247
248    /// Get the context associated with the float number
249    #[inline]
250    pub const fn context(&self) -> Context<R> {
251        self.context
252    }
253    /// Get a reference to the underlying numeric representation
254    #[inline]
255    pub const fn repr(&self) -> &Repr<B> {
256        &self.repr
257    }
258    /// Get the underlying numeric representation
259    ///
260    /// # Examples
261    ///
262    /// ```
263    /// # use dashu_float::DBig;
264    /// use dashu_float::Repr;
265    ///
266    /// let a = DBig::ONE;
267    /// assert_eq!(a.into_repr(), Repr::<10>::one());
268    /// ```
269    #[inline]
270    pub fn into_repr(self) -> Repr<B> {
271        self.repr
272    }
273
274    /// Convert raw parts (significand, exponent) into a float number.
275    ///
276    /// The precision will be inferred from significand (the lowest k such that `significand <= base^k`)
277    ///
278    /// # Examples
279    ///
280    /// ```
281    /// # use dashu_base::ParseError;
282    /// # use dashu_float::DBig;
283    /// use core::str::FromStr;
284    /// let a = DBig::from_parts((-1234).into(), -2);
285    /// assert_eq!(a, DBig::from_str("-12.34")?);
286    /// assert_eq!(a.precision(), 4); // 1234 has 4 (decimal) digits
287    /// # Ok::<(), ParseError>(())
288    /// ```
289    #[inline]
290    pub fn from_parts(significand: IBig, exponent: isize) -> Self {
291        let precision = digit_len::<B>(&significand).max(1); // set precision to 1 if signficand is zero
292        let repr = Repr::new(significand, exponent);
293        let context = Context::new(precision);
294        Self::new(repr, context)
295    }
296
297    /// Convert raw parts (significand, exponent) into a float number in a `const` context.
298    ///
299    /// It requires that the significand fits in a [DoubleWord].
300    ///
301    /// The precision will be inferred from significand (the lowest k such that `significand <= base^k`).
302    /// If the `min_precision` is provided, then the higher one from the given and inferred precision
303    /// will be used as the final precision.
304    ///
305    /// # Examples
306    ///
307    /// ```
308    /// # use dashu_base::ParseError;
309    /// # use dashu_float::DBig;
310    /// use core::str::FromStr;
311    /// use dashu_base::Sign;
312    ///
313    /// const A: DBig = DBig::from_parts_const(Sign::Negative, 1234, -2, None);
314    /// assert_eq!(A, DBig::from_str("-12.34")?);
315    /// assert_eq!(A.precision(), 4); // 1234 has 4 (decimal) digits
316    ///
317    /// const B: DBig = DBig::from_parts_const(Sign::Negative, 1234, -2, Some(5));
318    /// assert_eq!(B.precision(), 5); // overrided by the argument
319    /// # Ok::<(), ParseError>(())
320    /// ```
321    #[inline]
322    pub const fn from_parts_const(
323        sign: Sign,
324        mut significand: DoubleWord,
325        mut exponent: isize,
326        min_precision: Option<usize>,
327    ) -> Self {
328        if significand == 0 {
329            return Self::ZERO;
330        }
331
332        let mut digits = 0;
333
334        // normalize
335        if B.is_power_of_two() {
336            let base_bits = B.trailing_zeros();
337            let shift = significand.trailing_zeros() / base_bits;
338            significand >>= shift * base_bits;
339            exponent += shift as isize;
340            digits = ((DoubleWord::BITS - significand.leading_zeros() + base_bits - 1) / base_bits)
341                as usize;
342        } else {
343            let mut pow: DoubleWord = 1;
344            while significand % (B as DoubleWord) == 0 {
345                significand /= B as DoubleWord;
346                exponent += 1;
347            }
348            while let Some(next) = pow.checked_mul(B as DoubleWord) {
349                digits += 1;
350                if next > significand {
351                    break;
352                }
353                pow = next;
354            }
355        }
356
357        let repr = Repr {
358            significand: IBig::from_parts_const(sign, significand),
359            exponent,
360        };
361        let precision = match min_precision {
362            Some(prec) => {
363                if prec > digits {
364                    prec
365                } else {
366                    digits
367                }
368            }
369            None => digits,
370        };
371        Self::new(repr, Context::new(precision))
372    }
373
374    /// Return the value of the least significant digit of the float number x,
375    /// such that `x + ulp` is the first float number greater than x (given the precision from the context).
376    ///
377    /// # Examples
378    ///
379    /// ```
380    /// # use core::str::FromStr;
381    /// # use dashu_base::ParseError;
382    /// # use dashu_float::DBig;
383    /// assert_eq!(DBig::from_str("1.23")?.ulp(), DBig::from_str("0.01")?);
384    /// assert_eq!(DBig::from_str("01.23")?.ulp(), DBig::from_str("0.001")?);
385    /// # Ok::<(), ParseError>(())
386    /// ```
387    ///
388    /// # Panics
389    /// Panics if the precision of the number is 0 (unlimited).
390    ///
391    #[inline]
392    pub fn ulp(&self) -> Self {
393        if self.context.precision == 0 {
394            panic_unlimited_precision();
395        }
396        if self.repr.is_infinite() {
397            return self.clone();
398        }
399
400        let repr = Repr {
401            significand: IBig::ONE,
402            exponent: self.repr.exponent + self.repr.digits() as isize
403                - self.context.precision as isize,
404        };
405        Self::new(repr, self.context)
406    }
407
408    /// Similar to [FBig::ulp], but use approximated digits. It's guaranteed to be smaller than ulp(), for internal use only.
409    #[inline]
410    pub(crate) fn sub_ulp(&self) -> Self {
411        debug_assert!(self.context.precision != 0);
412        debug_assert!(self.repr.is_finite());
413
414        let repr = Repr {
415            significand: IBig::ONE,
416            exponent: self.repr.exponent + self.repr.digits_lb() as isize
417                - self.context.precision as isize
418                - 1,
419        };
420        Self::new(repr, self.context)
421    }
422}
423
424// This custom implementation is necessary due to https://github.com/rust-lang/rust/issues/98374
425impl<R: Round, const B: Word> Clone for FBig<R, B> {
426    #[inline]
427    fn clone(&self) -> Self {
428        Self {
429            repr: self.repr.clone(),
430            context: self.context,
431        }
432    }
433
434    #[inline]
435    fn clone_from(&mut self, source: &Self) {
436        self.repr.clone_from(&source.repr);
437        self.context = source.context;
438    }
439}
440
441impl<R: Round, const B: Word> Default for FBig<R, B> {
442    /// Default value: 0.
443    #[inline]
444    fn default() -> Self {
445        Self::ZERO
446    }
447}