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/// An `FBig` is a [`Repr`] (the value: significand × base<sup>exponent</sup>) paired with a
13/// [`Context`] (the precision cap and rounding mode). Arithmetic follows the associated context;
14/// use the [`Context`] methods directly when you need a different precision/rounding, or to receive
15/// the rounding direction and errors instead of a panic.
16///
17/// The generic parameters are `BASE` (`B`, in `[2, isize::MAX]`) and `RoundingMode` (`R`, chosen from
18/// the [`mode`] module). With the defaults the number is base 2 rounded towards zero (the most
19/// efficient format); [`DBig`](crate::DBig) aliases base 10 rounded to nearest.
20///
21/// Binary operators require both operands to share the same base and rounding mode (no hidden
22/// conversion is performed); comparison allows differing rounding modes but not differing bases.
23///
24/// See the [user guide](https://zyxin.xyz/dashu/types.html) for the
25/// memory layout, and the
26/// [construction](https://zyxin.xyz/dashu/construct.html),
27/// [parsing & printing](https://zyxin.xyz/dashu/io/parse.html),
28/// [IEEE 754 compliance](https://zyxin.xyz/dashu/compliance.html), and
29/// [conversion](https://zyxin.xyz/dashu/convert.html) pages for those
30/// topics. (Notably: `FBig` has no NaN, supports IEEE-754 signed zero, and treats infinities as
31/// terminal values.) The accepted string format is documented on the [`core::str::FromStr`] impl.
32///
33/// # Examples
34///
35/// ```
36/// # use dashu_base::ParseError;
37/// # use dashu_float::DBig;
38/// use core::str::FromStr;
39///
40/// // parsing
41/// let a = DBig::from_parts(123456789.into(), -5);
42/// let b = DBig::from_str("1234.56789")?;
43/// let c = DBig::from_str("1.23456789e3")?;
44/// assert_eq!(a, b);
45/// assert_eq!(b, c);
46///
47/// // printing
48/// assert_eq!(format!("{}", DBig::from_str("12.34")?), "12.34");
49/// let x = DBig::from_str("10.01")?
50/// .with_precision(0) // use unlimited precision
51/// .value();
52/// if dashu_int::Word::BITS == 64 {
53/// // number of digits to display depends on the word size
54/// assert_eq!(
55/// format!("{:?}", x.powi(100.into())),
56/// "1105115697720767968..1441386704950100001 * 10 ^ -200 (prec: 0)"
57/// );
58/// }
59/// # Ok::<(), ParseError>(())
60/// ```
61pub struct FBig<RoundingMode: Round = mode::Zero, const BASE: Word = 2> {
62 pub(crate) repr: Repr<BASE>,
63 pub(crate) context: Context<RoundingMode>,
64}
65
66impl<R: Round, const B: Word> FBig<R, B> {
67 /// Create a [FBig] instance from raw parts, internal use only
68 #[inline]
69 pub(crate) const fn new(repr: Repr<B>, context: Context<R>) -> Self {
70 Self { repr, context }
71 }
72
73 /// Create a [FBig] instance from [Repr] and [Context].
74 ///
75 /// This method should not be used in most cases. It's designed to be used when
76 /// you hold a [Repr] instance and want to create an [FBig] from that.
77 ///
78 /// # Examples
79 ///
80 /// ```
81 /// # use dashu_float::DBig;
82 /// use dashu_float::{Repr, Context};
83 ///
84 /// assert_eq!(DBig::from_repr(Repr::one(), Context::new(1)), DBig::ONE);
85 /// assert_eq!(DBig::from_repr(Repr::infinity(), Context::new(1)), DBig::INFINITY);
86 /// ```
87 ///
88 /// # Panics
89 ///
90 /// Panics if the [Repr] has more digits than `precision + 1` (the one allowed guard digit from
91 /// an inexact add/sub — see [`Repr`]). Note that this condition is not checked in release builds.
92 #[inline]
93 pub fn from_repr(repr: Repr<B>, context: Context<R>) -> Self {
94 debug_assert!(
95 repr.is_infinite() || !context.is_limited() || repr.digits() <= context.precision + 1
96 );
97 Self { repr, context }
98 }
99
100 /// Create a [FBig] instance from [Repr]. Due to the limitation of const operations,
101 /// the precision of the float is set to unlimited.
102 ///
103 /// # Examples
104 ///
105 /// ```
106 /// # use dashu_float::DBig;
107 /// use dashu_float::{Repr, Context};
108 ///
109 /// assert_eq!(DBig::from_repr_const(Repr::one()), DBig::ONE);
110 /// assert_eq!(DBig::from_repr_const(Repr::infinity()), DBig::INFINITY);
111 /// ```
112 #[inline]
113 pub const fn from_repr_const(repr: Repr<B>) -> Self {
114 Self {
115 repr,
116 context: Context::new(0),
117 }
118 }
119
120 /// [FBig] with value 0 and unlimited precision
121 ///
122 /// To test if the float number is `+0`, use `self.repr().is_pos_zero()` (or
123 /// `self.repr().significand().is_zero()` to detect either signed zero).
124 pub const ZERO: Self = Self::new(Repr::zero(), Context::new(0));
125
126 /// [FBig] with value 1 and unlimited precision
127 ///
128 /// To test if the float number is one, use `self.repr().is_one()`.
129 pub const ONE: Self = Self::new(Repr::one(), Context::new(0));
130
131 /// [FBig] with value -1 and unlimited precision
132 pub const NEG_ONE: Self = Self::new(Repr::neg_one(), Context::new(0));
133
134 /// [FBig] instance representing the positive infinity (+∞)
135 ///
136 /// To test if the float number is infinite, use `self.repr().infinite()`.
137 pub const INFINITY: Self = Self::new(Repr::infinity(), Context::new(0));
138
139 /// [FBig] instance representing the negative infinity (-∞)
140 ///
141 /// To test if the float number is infinite, use `self.repr().infinite()`.
142 pub const NEG_INFINITY: Self = Self::new(Repr::neg_infinity(), Context::new(0));
143
144 /// Get the maximum precision set for the float number.
145 ///
146 /// It's equivalent to `self.context().precision()`.
147 ///
148 /// # Examples
149 ///
150 /// ```
151 /// # use core::str::FromStr;
152 /// # use dashu_base::ParseError;
153 /// # use dashu_float::DBig;
154 /// # use dashu_int::IBig;
155 /// use dashu_float::Repr;
156 ///
157 /// let a = DBig::from_str("1.234")?;
158 /// assert!(a.repr().significand() <= &IBig::from(10).pow(a.precision()));
159 /// # Ok::<(), ParseError>(())
160 /// ```
161 #[inline]
162 pub const fn precision(&self) -> usize {
163 self.context.precision
164 }
165
166 /// Get the number of the significant digits in the float number
167 ///
168 /// It's equivalent to `self.repr().digits()`.
169 ///
170 /// This value is also the actual precision needed for the float number. Shrink to this
171 /// value using [with_precision()][FBig::with_precision] will not cause loss of float precision.
172 ///
173 /// # Examples
174 ///
175 /// ```
176 /// # use core::str::FromStr;
177 /// # use dashu_base::ParseError;
178 /// # use dashu_float::DBig;
179 /// use dashu_base::Approximation::*;
180 ///
181 /// let a = DBig::from_str("-1.234e-3")?;
182 /// assert_eq!(a.digits(), 4);
183 /// assert!(matches!(a.clone().with_precision(4), Exact(_)));
184 /// assert!(matches!(a.clone().with_precision(3), Inexact(_, _)));
185 /// # Ok::<(), ParseError>(())
186 /// ```
187 #[inline]
188 pub fn digits(&self) -> usize {
189 self.repr.digits()
190 }
191
192 /// Get the context associated with the float number
193 #[inline]
194 pub const fn context(&self) -> Context<R> {
195 self.context
196 }
197 /// Get a reference to the underlying numeric representation
198 #[inline]
199 pub const fn repr(&self) -> &Repr<B> {
200 &self.repr
201 }
202 /// Get the underlying numeric representation
203 ///
204 /// # Examples
205 ///
206 /// ```
207 /// # use dashu_float::DBig;
208 /// use dashu_float::Repr;
209 ///
210 /// let a = DBig::ONE;
211 /// assert_eq!(a.into_repr(), Repr::<10>::one());
212 /// ```
213 #[inline]
214 pub fn into_repr(self) -> Repr<B> {
215 self.repr
216 }
217
218 /// Convert raw parts (significand, exponent) into a float number.
219 ///
220 /// The precision will be inferred from significand (the lowest k such that `significand <= base^k`)
221 ///
222 /// # Examples
223 ///
224 /// ```
225 /// # use dashu_base::ParseError;
226 /// # use dashu_float::DBig;
227 /// use core::str::FromStr;
228 /// let a = DBig::from_parts((-1234).into(), -2);
229 /// assert_eq!(a, DBig::from_str("-12.34")?);
230 /// assert_eq!(a.precision(), 4); // 1234 has 4 (decimal) digits
231 /// # Ok::<(), ParseError>(())
232 /// ```
233 #[inline]
234 pub fn from_parts(significand: IBig, exponent: isize) -> Self {
235 let precision = digit_len::<B>(&significand).max(1); // set precision to 1 if signficand is zero
236 let repr = Repr::new(significand, exponent);
237 let context = Context::new(precision);
238 Self::new(repr, context)
239 }
240
241 /// Convert raw parts (significand, exponent) into a float number in a `const` context.
242 ///
243 /// It requires that the significand fits in a [DoubleWord].
244 ///
245 /// The precision will be inferred from significand (the lowest k such that `significand <= base^k`).
246 /// If the `min_precision` is provided, then the higher one from the given and inferred precision
247 /// will be used as the final precision.
248 ///
249 /// # Examples
250 ///
251 /// ```
252 /// # use dashu_base::ParseError;
253 /// # use dashu_float::DBig;
254 /// use core::str::FromStr;
255 /// use dashu_base::Sign;
256 ///
257 /// const A: DBig = DBig::from_parts_const(Sign::Negative, 1234, -2, None);
258 /// assert_eq!(A, DBig::from_str("-12.34")?);
259 /// assert_eq!(A.precision(), 4); // 1234 has 4 (decimal) digits
260 ///
261 /// const B: DBig = DBig::from_parts_const(Sign::Negative, 1234, -2, Some(5));
262 /// assert_eq!(B.precision(), 5); // overrided by the argument
263 /// # Ok::<(), ParseError>(())
264 /// ```
265 #[inline]
266 pub const fn from_parts_const(
267 sign: Sign,
268 mut significand: DoubleWord,
269 mut exponent: isize,
270 min_precision: Option<usize>,
271 ) -> Self {
272 if significand == 0 {
273 return Self::ZERO;
274 }
275
276 let mut digits = 0;
277
278 // normalize
279 if B.is_power_of_two() {
280 let base_bits = B.trailing_zeros();
281 let shift = significand.trailing_zeros() / base_bits;
282 significand >>= shift * base_bits;
283 exponent += shift as isize;
284 digits = ((DoubleWord::BITS - significand.leading_zeros() + base_bits - 1) / base_bits)
285 as usize;
286 } else {
287 let mut pow: DoubleWord = 1;
288 while significand % (B as DoubleWord) == 0 {
289 significand /= B as DoubleWord;
290 exponent += 1;
291 }
292 while let Some(next) = pow.checked_mul(B as DoubleWord) {
293 digits += 1;
294 if next > significand {
295 break;
296 }
297 pow = next;
298 }
299 }
300
301 let repr = Repr {
302 significand: IBig::from_parts_const(sign, significand),
303 exponent,
304 };
305 let precision = match min_precision {
306 Some(prec) => {
307 if prec > digits {
308 prec
309 } else {
310 digits
311 }
312 }
313 None => digits,
314 };
315 Self::new(repr, Context::new(precision))
316 }
317
318 /// Return the value of the least significant digit of the float number x,
319 /// such that `x + ulp` is the first float number greater than x (given the precision from the context).
320 ///
321 /// # Examples
322 ///
323 /// ```
324 /// # use core::str::FromStr;
325 /// # use dashu_base::ParseError;
326 /// # use dashu_float::DBig;
327 /// assert_eq!(DBig::from_str("1.23")?.ulp(), DBig::from_str("0.01")?);
328 /// assert_eq!(DBig::from_str("01.23")?.ulp(), DBig::from_str("0.001")?);
329 /// # Ok::<(), ParseError>(())
330 /// ```
331 ///
332 /// # Panics
333 /// Panics if the precision of the number is 0 (unlimited).
334 ///
335 #[inline]
336 pub fn ulp(&self) -> Self {
337 if self.context.precision == 0 {
338 panic_unlimited_precision();
339 }
340 if self.repr.is_infinite() {
341 return self.clone();
342 }
343
344 let repr = Repr {
345 significand: IBig::ONE,
346 exponent: self.repr.exponent + self.repr.digits() as isize
347 - self.context.precision as isize,
348 };
349 Self::new(repr, self.context)
350 }
351
352 /// Similar to [FBig::ulp], but use approximated digits. It's guaranteed to be smaller than ulp(), for internal use only.
353 #[inline]
354 pub(crate) fn sub_ulp(&self) -> Self {
355 debug_assert!(self.context.precision != 0);
356 debug_assert!(self.repr.is_finite());
357
358 let repr = Repr {
359 significand: IBig::ONE,
360 exponent: self.repr.exponent + self.repr.digits_lb() as isize
361 - self.context.precision as isize
362 - 1,
363 };
364 Self::new(repr, self.context)
365 }
366}
367
368// This custom implementation is necessary due to https://github.com/rust-lang/rust/issues/98374
369impl<R: Round, const B: Word> Clone for FBig<R, B> {
370 #[inline]
371 fn clone(&self) -> Self {
372 Self {
373 repr: self.repr.clone(),
374 context: self.context,
375 }
376 }
377
378 #[inline]
379 fn clone_from(&mut self, source: &Self) {
380 self.repr.clone_from(&source.repr);
381 self.context = source.context;
382 }
383}
384
385impl<R: Round, const B: Word> Default for FBig<R, B> {
386 /// Default value: 0.
387 #[inline]
388 fn default() -> Self {
389 Self::ZERO
390 }
391}