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}