lexical_parse_float/number.rs
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
7#![doc(hidden)]
8
9use crate::float::RawFloat;
10#[cfg(feature = "nightly")]
11use crate::fpu::set_precision;
12use lexical_util::format::NumberFormat;
13
14/// Representation of a number as the significant digits and exponent.
15///
16/// This is only used if the exponent base and the significant digit
17/// radix are the same, since we need to be able to move powers in and
18/// out of the exponent.
19#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
20pub struct Number<'a> {
21 /// The exponent of the float, scaled to the mantissa.
22 pub exponent: i64,
23 /// The significant digits of the float.
24 pub mantissa: u64,
25 /// If the float is negative.
26 pub is_negative: bool,
27 /// If the significant digits were truncated.
28 pub many_digits: bool,
29 /// The significant integer digits.
30 pub integer: &'a [u8],
31 /// The significant fraction digits.
32 pub fraction: Option<&'a [u8]>,
33}
34
35impl<'a> Number<'a> {
36 /// Detect if the float can be accurately reconstructed from native floats.
37 #[inline]
38 pub fn is_fast_path<F: RawFloat, const FORMAT: u128>(&self) -> bool {
39 let format = NumberFormat::<FORMAT> {};
40 debug_assert!(format.mantissa_radix() == format.exponent_base());
41 F::min_exponent_fast_path(format.radix()) <= self.exponent
42 && self.exponent <= F::max_exponent_disguised_fast_path(format.radix())
43 && self.mantissa <= F::MAX_MANTISSA_FAST_PATH
44 && !self.many_digits
45 }
46
47 /// The fast path algorithmn using machine-sized integers 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 // `set_precision` doesn't return a unit value on x87 FPUs.
57 #[allow(clippy::let_unit_value)]
58 pub fn try_fast_path<F: RawFloat, const FORMAT: u128>(&self) -> Option<F> {
59 let format = NumberFormat::<FORMAT> {};
60 debug_assert!(format.mantissa_radix() == format.exponent_base());
61 // The fast path crucially depends on arithmetic being rounded to the correct number of bits
62 // without any intermediate rounding. On x86 (without SSE or SSE2) this requires the precision
63 // of the x87 FPU stack to be changed so that it directly rounds to 64/32 bit.
64 // The `set_precision` function takes care of setting the precision on architectures which
65 // require setting it by changing the global state (like the control word of the x87 FPU).
66 #[cfg(feature = "nightly")]
67 let _cw = set_precision::<F>();
68
69 if self.is_fast_path::<F, FORMAT>() {
70 let radix = format.radix();
71 let max_exponent = F::max_exponent_fast_path(radix);
72 let mut value = if self.exponent <= max_exponent {
73 // normal fast path
74 let value = F::as_cast(self.mantissa);
75 if self.exponent < 0 {
76 // SAFETY: safe, since the `exponent <= max_exponent`.
77 value / unsafe { F::pow_fast_path((-self.exponent) as _, radix) }
78 } else {
79 // SAFETY: safe, since the `exponent <= max_exponent`.
80 value * unsafe { F::pow_fast_path(self.exponent as _, radix) }
81 }
82 } else {
83 // disguised fast path
84 let shift = self.exponent - max_exponent;
85 // SAFETY: safe, since `shift <= (max_disguised - max_exponent)`.
86 let int_power = unsafe { F::int_pow_fast_path(shift as usize, radix) };
87 let mantissa = self.mantissa.checked_mul(int_power)?;
88 if mantissa > F::MAX_MANTISSA_FAST_PATH {
89 return None;
90 }
91 // SAFETY: safe, since the `table.len() - 1 == max_exponent`.
92 F::as_cast(mantissa) * unsafe { F::pow_fast_path(max_exponent as _, radix) }
93 };
94 if self.is_negative {
95 value = -value;
96 }
97 Some(value)
98 } else {
99 None
100 }
101 }
102
103 /// Force a fast-path algorithm, even when it may not be accurate.
104 // `set_precision` doesn't return a unit value on x87 FPUs.
105 #[allow(clippy::let_unit_value)]
106 pub fn force_fast_path<F: RawFloat, const FORMAT: u128>(&self) -> F {
107 let format = NumberFormat::<FORMAT> {};
108 debug_assert!(format.mantissa_radix() == format.exponent_base());
109
110 #[cfg(feature = "nightly")]
111 let _cw = set_precision::<F>();
112
113 let radix = format.radix();
114 let mut value = F::as_cast(self.mantissa);
115 let max_exponent = F::max_exponent_fast_path(radix);
116 let mut exponent = self.exponent.abs();
117 if self.exponent < 0 {
118 while exponent > max_exponent {
119 // SAFETY: safe, since pow_fast_path is always safe for max_exponent.
120 value /= unsafe { F::pow_fast_path(max_exponent as _, radix) };
121 exponent -= max_exponent;
122 }
123 // SAFETY: safe, since the `exponent < max_exponent`.
124 value /= unsafe { F::pow_fast_path(exponent as _, radix) };
125 } else {
126 while exponent > max_exponent {
127 // SAFETY: safe, since pow_fast_path is always safe for max_exponent.
128 value *= unsafe { F::pow_fast_path(max_exponent as _, radix) };
129 exponent -= max_exponent;
130 }
131 // SAFETY: safe, since the `exponent < max_exponent`.
132 value *= unsafe { F::pow_fast_path(exponent as _, radix) };
133 }
134 if self.is_negative {
135 value = -value;
136 }
137 value
138 }
139}