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 ] |
30 | 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. |
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 | |