| 1 | //! Representation of a float as the significant digits and exponent. |
| 2 | |
| 3 | use crate::num::dec2flt::float::RawFloat; |
| 4 | use crate::num::dec2flt::fpu::set_precision; |
| 5 | |
| 6 | const INT_POW10: [u64; 16] = [ |
| 7 | 1, |
| 8 | 10, |
| 9 | 100, |
| 10 | 1000, |
| 11 | 10000, |
| 12 | 100000, |
| 13 | 1000000, |
| 14 | 10000000, |
| 15 | 100000000, |
| 16 | 1000000000, |
| 17 | 10000000000, |
| 18 | 100000000000, |
| 19 | 1000000000000, |
| 20 | 10000000000000, |
| 21 | 100000000000000, |
| 22 | 1000000000000000, |
| 23 | ]; |
| 24 | |
| 25 | /// A floating point number with up to 64 bits of mantissa and an `i64` exponent. |
| 26 | #[derive (Clone, Copy, Debug, Default, PartialEq, Eq)] |
| 27 | pub struct Decimal { |
| 28 | pub exponent: i64, |
| 29 | pub mantissa: u64, |
| 30 | pub negative: bool, |
| 31 | pub many_digits: bool, |
| 32 | } |
| 33 | |
| 34 | impl Decimal { |
| 35 | /// Detect if the float can be accurately reconstructed from native floats. |
| 36 | #[inline ] |
| 37 | fn can_use_fast_path<F: RawFloat>(&self) -> bool { |
| 38 | F::MIN_EXPONENT_FAST_PATH <= self.exponent |
| 39 | && self.exponent <= F::MAX_EXPONENT_DISGUISED_FAST_PATH |
| 40 | && self.mantissa <= F::MAX_MANTISSA_FAST_PATH |
| 41 | && !self.many_digits |
| 42 | } |
| 43 | |
| 44 | /// Try turning the decimal into an exact float representation, using machine-sized integers |
| 45 | /// and floats. |
| 46 | /// |
| 47 | /// This is extracted into a separate function so that it can be attempted before constructing |
| 48 | /// a Decimal. This only works if both the mantissa and the exponent |
| 49 | /// can be exactly represented as a machine float, since IEE-754 guarantees |
| 50 | /// no rounding will occur. |
| 51 | /// |
| 52 | /// There is an exception: disguised fast-path cases, where we can shift |
| 53 | /// powers-of-10 from the exponent to the significant digits. |
| 54 | pub fn try_fast_path<F: RawFloat>(&self) -> Option<F> { |
| 55 | // Here we need to work around <https://github.com/rust-lang/rust/issues/114479>. |
| 56 | // The fast path crucially depends on arithmetic being rounded to the correct number of bits |
| 57 | // without any intermediate rounding. On x86 (without SSE or SSE2) this requires the precision |
| 58 | // of the x87 FPU stack to be changed so that it directly rounds to 64/32 bit. |
| 59 | // The `set_precision` function takes care of setting the precision on architectures which |
| 60 | // require setting it by changing the global state (like the control word of the x87 FPU). |
| 61 | let _cw = set_precision::<F>(); |
| 62 | |
| 63 | if !self.can_use_fast_path::<F>() { |
| 64 | return None; |
| 65 | } |
| 66 | |
| 67 | let value = if self.exponent <= F::MAX_EXPONENT_FAST_PATH { |
| 68 | // normal fast path |
| 69 | let value = F::from_u64(self.mantissa); |
| 70 | if self.exponent < 0 { |
| 71 | value / F::pow10_fast_path((-self.exponent) as _) |
| 72 | } else { |
| 73 | value * F::pow10_fast_path(self.exponent as _) |
| 74 | } |
| 75 | } else { |
| 76 | // disguised fast path |
| 77 | let shift = self.exponent - F::MAX_EXPONENT_FAST_PATH; |
| 78 | let mantissa = self.mantissa.checked_mul(INT_POW10[shift as usize])?; |
| 79 | if mantissa > F::MAX_MANTISSA_FAST_PATH { |
| 80 | return None; |
| 81 | } |
| 82 | F::from_u64(mantissa) * F::pow10_fast_path(F::MAX_EXPONENT_FAST_PATH as _) |
| 83 | }; |
| 84 | |
| 85 | if self.negative { Some(-value) } else { Some(value) } |
| 86 | } |
| 87 | } |
| 88 | |