1//! Representation of a float as the significant digits and exponent.
2
3use crate::num::dec2flt::float::RawFloat;
4use crate::num::dec2flt::fpu::set_precision;
5
6#[rustfmt::skip]
7const 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)]
27pub struct Number {
28 pub exponent: i64,
29 pub mantissa: u64,
30 pub negative: bool,
31 pub many_digits: bool,
32}
33
34impl 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