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