mesh_loader/utils/float/
decimal.rs

1//! Arbitrary-precision decimal class for fallback algorithms.
2//!
3//! This is only used if the fast-path (native floats) and
4//! the Eisel-Lemire algorithm are unable to unambiguously
5//! determine the float.
6//!
7//! The technique used is "Simple Decimal Conversion", developed
8//! by Nigel Tao and Ken Thompson. A detailed description of the
9//! algorithm can be found in "ParseNumberF64 by Simple Decimal Conversion",
10//! available online: <https://nigeltao.github.io/blog/2020/parse-number-f64-simple.html>.
11
12use super::common::{is_8digits, ByteSlice};
13
14#[derive(Clone)]
15pub(crate) struct Decimal {
16    /// The number of significant digits in the decimal.
17    pub(crate) num_digits: usize,
18    /// The offset of the decimal point in the significant digits.
19    pub(crate) decimal_point: i32,
20    /// If the number of significant digits stored in the decimal is truncated.
21    pub(crate) truncated: bool,
22    /// Buffer of the raw digits, in the range [0, 9].
23    pub(crate) digits: [u8; Self::MAX_DIGITS],
24}
25
26impl Decimal {
27    const fn default() -> Self {
28        Self {
29            num_digits: 0,
30            decimal_point: 0,
31            truncated: false,
32            digits: [0; Self::MAX_DIGITS],
33        }
34    }
35}
36
37impl Decimal {
38    /// The maximum number of digits required to unambiguously round a float.
39    ///
40    /// For a double-precision IEEE 754 float, this required 767 digits,
41    /// so we store the max digits + 1.
42    ///
43    /// We can exactly represent a float in radix `b` from radix 2 if
44    /// `b` is divisible by 2. This function calculates the exact number of
45    /// digits required to exactly represent that float.
46    ///
47    /// According to the "Handbook of Floating Point Arithmetic",
48    /// for IEEE754, with emin being the min exponent, p2 being the
49    /// precision, and b being the radix, the number of digits follows as:
50    ///
51    /// `−emin + p2 + ⌊(emin + 1) log(2, b) − log(1 − 2^(−p2), b)⌋`
52    ///
53    /// For f32, this follows as:
54    ///     emin = -126
55    ///     p2 = 24
56    ///
57    /// For f64, this follows as:
58    ///     emin = -1022
59    ///     p2 = 53
60    ///
61    /// In Python:
62    ///     `-emin + p2 + math.floor((emin+ 1)*math.log(2, b)-math.log(1-2**(-p2), b))`
63    pub(crate) const MAX_DIGITS: usize = 768;
64    /// The max digits that can be exactly represented in a 64-bit integer.
65    pub(crate) const MAX_DIGITS_WITHOUT_OVERFLOW: usize = 19;
66    pub(crate) const DECIMAL_POINT_RANGE: i32 = 2047;
67
68    /// Append a digit to the buffer.
69    pub(crate) fn try_add_digit(&mut self, digit: u8) {
70        if self.num_digits < Self::MAX_DIGITS {
71            self.digits[self.num_digits] = digit;
72        }
73        self.num_digits += 1;
74    }
75
76    /// Trim trailing zeros from the buffer.
77    pub(crate) fn trim(&mut self) {
78        // All of the following calls to `Decimal::trim` can't panic because:
79        //
80        //  1. `parse_decimal` sets `num_digits` to a max of `Decimal::MAX_DIGITS`.
81        //  2. `right_shift` sets `num_digits` to `write_index`, which is bounded by `num_digits`.
82        //  3. `left_shift` `num_digits` to a max of `Decimal::MAX_DIGITS`.
83        //
84        // Trim is only called in `right_shift` and `left_shift`.
85        debug_assert!(self.num_digits <= Self::MAX_DIGITS);
86        while self.num_digits != 0 && self.digits[self.num_digits - 1] == 0 {
87            self.num_digits -= 1;
88        }
89    }
90
91    pub(crate) fn round(&self) -> u64 {
92        if self.num_digits == 0 || self.decimal_point < 0 {
93            return 0;
94        } else if self.decimal_point > 18 {
95            return 0xFFFF_FFFF_FFFF_FFFF_u64;
96        }
97        let dp = self.decimal_point as usize;
98        let mut n = 0_u64;
99        for i in 0..dp {
100            n *= 10;
101            if i < self.num_digits {
102                n += self.digits[i] as u64;
103            }
104        }
105        let mut round_up = false;
106        if dp < self.num_digits {
107            round_up = self.digits[dp] >= 5;
108            if self.digits[dp] == 5 && dp + 1 == self.num_digits {
109                round_up = self.truncated || ((dp != 0) && (1 & self.digits[dp - 1] != 0));
110            }
111        }
112        if round_up {
113            n += 1;
114        }
115        n
116    }
117
118    /// Computes decimal * 2^shift.
119    pub(crate) fn left_shift(&mut self, shift: usize) {
120        if self.num_digits == 0 {
121            return;
122        }
123        let num_new_digits = number_of_digits_decimal_left_shift(self, shift);
124        let mut read_index = self.num_digits;
125        let mut write_index = self.num_digits + num_new_digits;
126        let mut n = 0_u64;
127        while read_index != 0 {
128            read_index -= 1;
129            write_index -= 1;
130            n += (self.digits[read_index] as u64) << shift;
131            let quotient = n / 10;
132            let remainder = n - (10 * quotient);
133            if write_index < Self::MAX_DIGITS {
134                self.digits[write_index] = remainder as u8;
135            } else if remainder > 0 {
136                self.truncated = true;
137            }
138            n = quotient;
139        }
140        while n > 0 {
141            write_index -= 1;
142            let quotient = n / 10;
143            let remainder = n - (10 * quotient);
144            if write_index < Self::MAX_DIGITS {
145                self.digits[write_index] = remainder as u8;
146            } else if remainder > 0 {
147                self.truncated = true;
148            }
149            n = quotient;
150        }
151        self.num_digits += num_new_digits;
152        if self.num_digits > Self::MAX_DIGITS {
153            self.num_digits = Self::MAX_DIGITS;
154        }
155        self.decimal_point += num_new_digits as i32;
156        self.trim();
157    }
158
159    /// Computes decimal * 2^-shift.
160    pub(crate) fn right_shift(&mut self, shift: usize) {
161        let mut read_index = 0;
162        let mut write_index = 0;
163        let mut n = 0_u64;
164        while (n >> shift) == 0 {
165            if read_index < self.num_digits {
166                n = (10 * n) + self.digits[read_index] as u64;
167                read_index += 1;
168            } else if n == 0 {
169                return;
170            } else {
171                while (n >> shift) == 0 {
172                    n *= 10;
173                    read_index += 1;
174                }
175                break;
176            }
177        }
178        self.decimal_point -= read_index as i32 - 1;
179        if self.decimal_point < -Self::DECIMAL_POINT_RANGE {
180            // `self = Self::Default()`, but without the overhead of clearing `digits`.
181            self.num_digits = 0;
182            self.decimal_point = 0;
183            self.truncated = false;
184            return;
185        }
186        let mask = (1_u64 << shift) - 1;
187        while read_index < self.num_digits {
188            let new_digit = (n >> shift) as u8;
189            n = (10 * (n & mask)) + self.digits[read_index] as u64;
190            read_index += 1;
191            self.digits[write_index] = new_digit;
192            write_index += 1;
193        }
194        while n > 0 {
195            let new_digit = (n >> shift) as u8;
196            n = 10 * (n & mask);
197            if write_index < Self::MAX_DIGITS {
198                self.digits[write_index] = new_digit;
199                write_index += 1;
200            } else if new_digit > 0 {
201                self.truncated = true;
202            }
203        }
204        self.num_digits = write_index;
205        self.trim();
206    }
207}
208
209/// Parse a big integer representation of the float as a decimal.
210pub(crate) fn parse_decimal(mut s: &[u8]) -> Decimal {
211    let mut d = Decimal::default();
212    let start = s;
213
214    while let Some((&b'0', s_next)) = s.split_first() {
215        s = s_next;
216    }
217
218    s = s.parse_digits(|digit| d.try_add_digit(digit));
219
220    if let Some((b'.', s_next)) = s.split_first() {
221        s = s_next;
222        let first = s;
223        // Skip leading zeros.
224        if d.num_digits == 0 {
225            while let Some((&b'0', s_next)) = s.split_first() {
226                s = s_next;
227            }
228        }
229        while s.len() >= 8 && d.num_digits + 8 < Decimal::MAX_DIGITS {
230            let v = s.read_u64le();
231            if !is_8digits(v) {
232                break;
233            }
234            d.digits[d.num_digits..].write_u64le(v - 0x3030_3030_3030_3030);
235            d.num_digits += 8;
236            s = &s[8..];
237        }
238        s = s.parse_digits(|digit| d.try_add_digit(digit));
239        d.decimal_point = s.len() as i32 - first.len() as i32;
240    }
241    if d.num_digits != 0 {
242        // Ignore the trailing zeros if there are any
243        let mut n_trailing_zeros = 0;
244        for &c in start[..(start.len() - s.len())].iter().rev() {
245            if c == b'0' {
246                n_trailing_zeros += 1;
247            } else if c != b'.' {
248                break;
249            }
250        }
251        d.decimal_point += n_trailing_zeros as i32;
252        d.num_digits -= n_trailing_zeros;
253        d.decimal_point += d.num_digits as i32;
254        if d.num_digits > Decimal::MAX_DIGITS {
255            d.truncated = true;
256            d.num_digits = Decimal::MAX_DIGITS;
257        }
258    }
259    if let Some((&ch, s_next)) = s.split_first() {
260        if ch == b'e' || ch == b'E' {
261            s = s_next;
262            let mut neg_exp = false;
263            if let Some((&ch, s_next)) = s.split_first() {
264                neg_exp = ch == b'-';
265                if ch == b'-' || ch == b'+' {
266                    s = s_next;
267                }
268            }
269            let mut exp_num = 0_i32;
270
271            s.parse_digits(|digit| {
272                if exp_num < 0x10000 {
273                    exp_num = 10 * exp_num + digit as i32;
274                }
275            });
276
277            d.decimal_point += if neg_exp { -exp_num } else { exp_num };
278        }
279    }
280    for i in d.num_digits..Decimal::MAX_DIGITS_WITHOUT_OVERFLOW {
281        d.digits[i] = 0;
282    }
283    d
284}
285
286fn number_of_digits_decimal_left_shift(d: &Decimal, mut shift: usize) -> usize {
287    #[rustfmt::skip]
288    static TABLE: [u16; 65] = [
289        0x0000, 0x0800, 0x0801, 0x0803, 0x1006, 0x1009, 0x100D, 0x1812, 0x1817, 0x181D, 0x2024,
290        0x202B, 0x2033, 0x203C, 0x2846, 0x2850, 0x285B, 0x3067, 0x3073, 0x3080, 0x388E, 0x389C,
291        0x38AB, 0x38BB, 0x40CC, 0x40DD, 0x40EF, 0x4902, 0x4915, 0x4929, 0x513E, 0x5153, 0x5169,
292        0x5180, 0x5998, 0x59B0, 0x59C9, 0x61E3, 0x61FD, 0x6218, 0x6A34, 0x6A50, 0x6A6D, 0x6A8B,
293        0x72AA, 0x72C9, 0x72E9, 0x7B0A, 0x7B2B, 0x7B4D, 0x8370, 0x8393, 0x83B7, 0x83DC, 0x8C02,
294        0x8C28, 0x8C4F, 0x9477, 0x949F, 0x94C8, 0x9CF2, 0x051C, 0x051C, 0x051C, 0x051C,
295    ];
296    #[rustfmt::skip]
297    static TABLE_POW5: [u8; 0x051C] = [
298        5, 2, 5, 1, 2, 5, 6, 2, 5, 3, 1, 2, 5, 1, 5, 6, 2, 5, 7, 8, 1, 2, 5, 3, 9, 0, 6, 2, 5, 1,
299        9, 5, 3, 1, 2, 5, 9, 7, 6, 5, 6, 2, 5, 4, 8, 8, 2, 8, 1, 2, 5, 2, 4, 4, 1, 4, 0, 6, 2, 5,
300        1, 2, 2, 0, 7, 0, 3, 1, 2, 5, 6, 1, 0, 3, 5, 1, 5, 6, 2, 5, 3, 0, 5, 1, 7, 5, 7, 8, 1, 2,
301        5, 1, 5, 2, 5, 8, 7, 8, 9, 0, 6, 2, 5, 7, 6, 2, 9, 3, 9, 4, 5, 3, 1, 2, 5, 3, 8, 1, 4, 6,
302        9, 7, 2, 6, 5, 6, 2, 5, 1, 9, 0, 7, 3, 4, 8, 6, 3, 2, 8, 1, 2, 5, 9, 5, 3, 6, 7, 4, 3, 1,
303        6, 4, 0, 6, 2, 5, 4, 7, 6, 8, 3, 7, 1, 5, 8, 2, 0, 3, 1, 2, 5, 2, 3, 8, 4, 1, 8, 5, 7, 9,
304        1, 0, 1, 5, 6, 2, 5, 1, 1, 9, 2, 0, 9, 2, 8, 9, 5, 5, 0, 7, 8, 1, 2, 5, 5, 9, 6, 0, 4, 6,
305        4, 4, 7, 7, 5, 3, 9, 0, 6, 2, 5, 2, 9, 8, 0, 2, 3, 2, 2, 3, 8, 7, 6, 9, 5, 3, 1, 2, 5, 1,
306        4, 9, 0, 1, 1, 6, 1, 1, 9, 3, 8, 4, 7, 6, 5, 6, 2, 5, 7, 4, 5, 0, 5, 8, 0, 5, 9, 6, 9, 2,
307        3, 8, 2, 8, 1, 2, 5, 3, 7, 2, 5, 2, 9, 0, 2, 9, 8, 4, 6, 1, 9, 1, 4, 0, 6, 2, 5, 1, 8, 6,
308        2, 6, 4, 5, 1, 4, 9, 2, 3, 0, 9, 5, 7, 0, 3, 1, 2, 5, 9, 3, 1, 3, 2, 2, 5, 7, 4, 6, 1, 5,
309        4, 7, 8, 5, 1, 5, 6, 2, 5, 4, 6, 5, 6, 6, 1, 2, 8, 7, 3, 0, 7, 7, 3, 9, 2, 5, 7, 8, 1, 2,
310        5, 2, 3, 2, 8, 3, 0, 6, 4, 3, 6, 5, 3, 8, 6, 9, 6, 2, 8, 9, 0, 6, 2, 5, 1, 1, 6, 4, 1, 5,
311        3, 2, 1, 8, 2, 6, 9, 3, 4, 8, 1, 4, 4, 5, 3, 1, 2, 5, 5, 8, 2, 0, 7, 6, 6, 0, 9, 1, 3, 4,
312        6, 7, 4, 0, 7, 2, 2, 6, 5, 6, 2, 5, 2, 9, 1, 0, 3, 8, 3, 0, 4, 5, 6, 7, 3, 3, 7, 0, 3, 6,
313        1, 3, 2, 8, 1, 2, 5, 1, 4, 5, 5, 1, 9, 1, 5, 2, 2, 8, 3, 6, 6, 8, 5, 1, 8, 0, 6, 6, 4, 0,
314        6, 2, 5, 7, 2, 7, 5, 9, 5, 7, 6, 1, 4, 1, 8, 3, 4, 2, 5, 9, 0, 3, 3, 2, 0, 3, 1, 2, 5, 3,
315        6, 3, 7, 9, 7, 8, 8, 0, 7, 0, 9, 1, 7, 1, 2, 9, 5, 1, 6, 6, 0, 1, 5, 6, 2, 5, 1, 8, 1, 8,
316        9, 8, 9, 4, 0, 3, 5, 4, 5, 8, 5, 6, 4, 7, 5, 8, 3, 0, 0, 7, 8, 1, 2, 5, 9, 0, 9, 4, 9, 4,
317        7, 0, 1, 7, 7, 2, 9, 2, 8, 2, 3, 7, 9, 1, 5, 0, 3, 9, 0, 6, 2, 5, 4, 5, 4, 7, 4, 7, 3, 5,
318        0, 8, 8, 6, 4, 6, 4, 1, 1, 8, 9, 5, 7, 5, 1, 9, 5, 3, 1, 2, 5, 2, 2, 7, 3, 7, 3, 6, 7, 5,
319        4, 4, 3, 2, 3, 2, 0, 5, 9, 4, 7, 8, 7, 5, 9, 7, 6, 5, 6, 2, 5, 1, 1, 3, 6, 8, 6, 8, 3, 7,
320        7, 2, 1, 6, 1, 6, 0, 2, 9, 7, 3, 9, 3, 7, 9, 8, 8, 2, 8, 1, 2, 5, 5, 6, 8, 4, 3, 4, 1, 8,
321        8, 6, 0, 8, 0, 8, 0, 1, 4, 8, 6, 9, 6, 8, 9, 9, 4, 1, 4, 0, 6, 2, 5, 2, 8, 4, 2, 1, 7, 0,
322        9, 4, 3, 0, 4, 0, 4, 0, 0, 7, 4, 3, 4, 8, 4, 4, 9, 7, 0, 7, 0, 3, 1, 2, 5, 1, 4, 2, 1, 0,
323        8, 5, 4, 7, 1, 5, 2, 0, 2, 0, 0, 3, 7, 1, 7, 4, 2, 2, 4, 8, 5, 3, 5, 1, 5, 6, 2, 5, 7, 1,
324        0, 5, 4, 2, 7, 3, 5, 7, 6, 0, 1, 0, 0, 1, 8, 5, 8, 7, 1, 1, 2, 4, 2, 6, 7, 5, 7, 8, 1, 2,
325        5, 3, 5, 5, 2, 7, 1, 3, 6, 7, 8, 8, 0, 0, 5, 0, 0, 9, 2, 9, 3, 5, 5, 6, 2, 1, 3, 3, 7, 8,
326        9, 0, 6, 2, 5, 1, 7, 7, 6, 3, 5, 6, 8, 3, 9, 4, 0, 0, 2, 5, 0, 4, 6, 4, 6, 7, 7, 8, 1, 0,
327        6, 6, 8, 9, 4, 5, 3, 1, 2, 5, 8, 8, 8, 1, 7, 8, 4, 1, 9, 7, 0, 0, 1, 2, 5, 2, 3, 2, 3, 3,
328        8, 9, 0, 5, 3, 3, 4, 4, 7, 2, 6, 5, 6, 2, 5, 4, 4, 4, 0, 8, 9, 2, 0, 9, 8, 5, 0, 0, 6, 2,
329        6, 1, 6, 1, 6, 9, 4, 5, 2, 6, 6, 7, 2, 3, 6, 3, 2, 8, 1, 2, 5, 2, 2, 2, 0, 4, 4, 6, 0, 4,
330        9, 2, 5, 0, 3, 1, 3, 0, 8, 0, 8, 4, 7, 2, 6, 3, 3, 3, 6, 1, 8, 1, 6, 4, 0, 6, 2, 5, 1, 1,
331        1, 0, 2, 2, 3, 0, 2, 4, 6, 2, 5, 1, 5, 6, 5, 4, 0, 4, 2, 3, 6, 3, 1, 6, 6, 8, 0, 9, 0, 8,
332        2, 0, 3, 1, 2, 5, 5, 5, 5, 1, 1, 1, 5, 1, 2, 3, 1, 2, 5, 7, 8, 2, 7, 0, 2, 1, 1, 8, 1, 5,
333        8, 3, 4, 0, 4, 5, 4, 1, 0, 1, 5, 6, 2, 5, 2, 7, 7, 5, 5, 5, 7, 5, 6, 1, 5, 6, 2, 8, 9, 1,
334        3, 5, 1, 0, 5, 9, 0, 7, 9, 1, 7, 0, 2, 2, 7, 0, 5, 0, 7, 8, 1, 2, 5, 1, 3, 8, 7, 7, 7, 8,
335        7, 8, 0, 7, 8, 1, 4, 4, 5, 6, 7, 5, 5, 2, 9, 5, 3, 9, 5, 8, 5, 1, 1, 3, 5, 2, 5, 3, 9, 0,
336        6, 2, 5, 6, 9, 3, 8, 8, 9, 3, 9, 0, 3, 9, 0, 7, 2, 2, 8, 3, 7, 7, 6, 4, 7, 6, 9, 7, 9, 2,
337        5, 5, 6, 7, 6, 2, 6, 9, 5, 3, 1, 2, 5, 3, 4, 6, 9, 4, 4, 6, 9, 5, 1, 9, 5, 3, 6, 1, 4, 1,
338        8, 8, 8, 2, 3, 8, 4, 8, 9, 6, 2, 7, 8, 3, 8, 1, 3, 4, 7, 6, 5, 6, 2, 5, 1, 7, 3, 4, 7, 2,
339        3, 4, 7, 5, 9, 7, 6, 8, 0, 7, 0, 9, 4, 4, 1, 1, 9, 2, 4, 4, 8, 1, 3, 9, 1, 9, 0, 6, 7, 3,
340        8, 2, 8, 1, 2, 5, 8, 6, 7, 3, 6, 1, 7, 3, 7, 9, 8, 8, 4, 0, 3, 5, 4, 7, 2, 0, 5, 9, 6, 2,
341        2, 4, 0, 6, 9, 5, 9, 5, 3, 3, 6, 9, 1, 4, 0, 6, 2, 5,
342    ];
343
344    shift &= 63;
345    let x_a = TABLE[shift];
346    let x_b = TABLE[shift + 1];
347    let num_new_digits = (x_a >> 11) as usize;
348    let pow5_a = (0x7FF & x_a) as usize;
349    let pow5_b = (0x7FF & x_b) as usize;
350    let pow5 = &TABLE_POW5[pow5_a..];
351    for (i, &p5) in pow5.iter().enumerate().take(pow5_b - pow5_a) {
352        if i >= d.num_digits {
353            return num_new_digits - 1;
354        } else if d.digits[i] == p5 {
355            continue;
356        } else if d.digits[i] < p5 {
357            return num_new_digits - 1;
358        } else {
359            return num_new_digits;
360        }
361    }
362    num_new_digits
363}