1 //! Representation of a float as the significant digits and exponent. 2 //! 3 //! This is adapted from [fast-float-rust](https://github.com/aldanor/fast-float-rust), 4 //! a port of [fast_float](https://github.com/fastfloat/fast_float) to Rust. 5 6 #![doc(hidden)] 7 8 #[cfg(feature = "nightly")] 9 use crate::fpu::set_precision; 10 use crate::num::Float; 11 12 /// Representation of a number as the significant digits and exponent. 13 /// 14 /// This is only used if the exponent base and the significant digit 15 /// radix are the same, since we need to be able to move powers in and 16 /// out of the exponent. 17 #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] 18 pub struct Number { 19 /// The exponent of the float, scaled to the mantissa. 20 pub exponent: i32, 21 /// The significant digits of the float. 22 pub mantissa: u64, 23 /// If the significant digits were truncated. 24 pub many_digits: bool, 25 } 26 27 impl Number { 28 /// Detect if the float can be accurately reconstructed from native floats. 29 #[inline] is_fast_path<F: Float>(&self) -> bool30 pub fn is_fast_path<F: Float>(&self) -> bool { 31 F::MIN_EXPONENT_FAST_PATH <= self.exponent 32 && self.exponent <= F::MAX_EXPONENT_DISGUISED_FAST_PATH 33 && self.mantissa <= F::MAX_MANTISSA_FAST_PATH 34 && !self.many_digits 35 } 36 37 /// The fast path algorithmn using machine-sized integers and floats. 38 /// 39 /// This is extracted into a separate function so that it can be attempted before constructing 40 /// a Decimal. This only works if both the mantissa and the exponent 41 /// can be exactly represented as a machine float, since IEE-754 guarantees 42 /// no rounding will occur. 43 /// 44 /// There is an exception: disguised fast-path cases, where we can shift 45 /// powers-of-10 from the exponent to the significant digits. try_fast_path<F: Float>(&self) -> Option<F>46 pub fn try_fast_path<F: Float>(&self) -> Option<F> { 47 // The fast path crucially depends on arithmetic being rounded to the correct number of bits 48 // without any intermediate rounding. On x86 (without SSE or SSE2) this requires the precision 49 // of the x87 FPU stack to be changed so that it directly rounds to 64/32 bit. 50 // The `set_precision` function takes care of setting the precision on architectures which 51 // require setting it by changing the global state (like the control word of the x87 FPU). 52 #[cfg(feature = "nightly")] 53 let _cw = set_precision::<F>(); 54 55 if self.is_fast_path::<F>() { 56 let max_exponent = F::MAX_EXPONENT_FAST_PATH; 57 Some(if self.exponent <= max_exponent { 58 // normal fast path 59 let value = F::from_u64(self.mantissa); 60 if self.exponent < 0 { 61 // SAFETY: safe, since the `exponent <= max_exponent`. 62 value / unsafe { F::pow_fast_path((-self.exponent) as _) } 63 } else { 64 // SAFETY: safe, since the `exponent <= max_exponent`. 65 value * unsafe { F::pow_fast_path(self.exponent as _) } 66 } 67 } else { 68 // disguised fast path 69 let shift = self.exponent - max_exponent; 70 // SAFETY: safe, since `shift <= (max_disguised - max_exponent)`. 71 let int_power = unsafe { F::int_pow_fast_path(shift as usize, 10) }; 72 let mantissa = self.mantissa.checked_mul(int_power)?; 73 if mantissa > F::MAX_MANTISSA_FAST_PATH { 74 return None; 75 } 76 // SAFETY: safe, since the `table.len() - 1 == max_exponent`. 77 F::from_u64(mantissa) * unsafe { F::pow_fast_path(max_exponent as _) } 78 }) 79 } else { 80 None 81 } 82 } 83 } 84