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}