decimal.rs - source
Description: Source of the Rust file `library/core/src/num/dec2flt/decimal.rs`.

#
core/num/dec2flt/
decimal.rs

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