Skip to main content

core/num/imp/dec2flt/
decimal.rs

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