rust_decimal/ops/
common.rs

1use crate::constants::{MAX_I32_SCALE, MAX_SCALE_I32, POWERS_10};
2use crate::Decimal;
3
4#[derive(Debug)]
5pub struct Buf12 {
6    pub data: [u32; 3],
7}
8
9impl Buf12 {
10    pub(super) const fn from_dec64(value: &Dec64) -> Self {
11        Buf12 {
12            data: [value.low64 as u32, (value.low64 >> 32) as u32, value.hi],
13        }
14    }
15
16    pub(super) const fn from_decimal(value: &Decimal) -> Self {
17        Buf12 {
18            data: value.mantissa_array3(),
19        }
20    }
21
22    #[inline(always)]
23    pub const fn lo(&self) -> u32 {
24        self.data[0]
25    }
26    #[inline(always)]
27    pub const fn mid(&self) -> u32 {
28        self.data[1]
29    }
30    #[inline(always)]
31    pub const fn hi(&self) -> u32 {
32        self.data[2]
33    }
34    #[inline(always)]
35    pub fn set_lo(&mut self, value: u32) {
36        self.data[0] = value;
37    }
38    #[inline(always)]
39    pub fn set_mid(&mut self, value: u32) {
40        self.data[1] = value;
41    }
42    #[inline(always)]
43    pub fn set_hi(&mut self, value: u32) {
44        self.data[2] = value;
45    }
46
47    #[inline(always)]
48    pub const fn low64(&self) -> u64 {
49        ((self.data[1] as u64) << 32) | (self.data[0] as u64)
50    }
51
52    #[inline(always)]
53    pub fn set_low64(&mut self, value: u64) {
54        self.data[1] = (value >> 32) as u32;
55        self.data[0] = value as u32;
56    }
57
58    #[inline(always)]
59    pub const fn high64(&self) -> u64 {
60        ((self.data[2] as u64) << 32) | (self.data[1] as u64)
61    }
62
63    #[inline(always)]
64    pub fn set_high64(&mut self, value: u64) {
65        self.data[2] = (value >> 32) as u32;
66        self.data[1] = value as u32;
67    }
68
69    // Determine the maximum value of x that ensures that the quotient when scaled up by 10^x
70    // still fits in 96 bits. Ultimately, we want to make scale positive - if we can't then
71    // we're going to overflow. Because x is ultimately used to lookup inside the POWERS array, it
72    // must be a valid value 0 <= x <= 9
73    pub fn find_scale(&self, scale: i32) -> Option<usize> {
74        const OVERFLOW_MAX_9_HI: u32 = 4;
75        const OVERFLOW_MAX_8_HI: u32 = 42;
76        const OVERFLOW_MAX_7_HI: u32 = 429;
77        const OVERFLOW_MAX_6_HI: u32 = 4294;
78        const OVERFLOW_MAX_5_HI: u32 = 42949;
79        const OVERFLOW_MAX_4_HI: u32 = 429496;
80        const OVERFLOW_MAX_3_HI: u32 = 4294967;
81        const OVERFLOW_MAX_2_HI: u32 = 42949672;
82        const OVERFLOW_MAX_1_HI: u32 = 429496729;
83        const OVERFLOW_MAX_9_LOW64: u64 = 5441186219426131129;
84
85        let hi = self.data[2];
86        let low64 = self.low64();
87        let mut x = 0usize;
88
89        // Quick check to stop us from trying to scale any more.
90        //
91        if hi > OVERFLOW_MAX_1_HI {
92            // If it's less than 0, which it probably is - overflow. We can't do anything.
93            if scale < 0 {
94                return None;
95            }
96            return Some(x);
97        }
98
99        if scale > MAX_SCALE_I32 - 9 {
100            // We can't scale by 10^9 without exceeding the max scale factor.
101            // Instead, we'll try to scale by the most that we can and see if that works.
102            // This is safe to do due to the check above. e.g. scale > 19 in the above, so it will
103            // evaluate to 9 or less below.
104            x = (MAX_SCALE_I32 - scale) as usize;
105            if hi < POWER_OVERFLOW_VALUES[x - 1].data[2] {
106                if x as i32 + scale < 0 {
107                    // We still overflow
108                    return None;
109                }
110                return Some(x);
111            }
112        } else if hi < OVERFLOW_MAX_9_HI || hi == OVERFLOW_MAX_9_HI && low64 <= OVERFLOW_MAX_9_LOW64 {
113            return Some(9);
114        }
115
116        // Do a binary search to find a power to scale by that is less than 9
117        x = if hi > OVERFLOW_MAX_5_HI {
118            if hi > OVERFLOW_MAX_3_HI {
119                if hi > OVERFLOW_MAX_2_HI {
120                    1
121                } else {
122                    2
123                }
124            } else if hi > OVERFLOW_MAX_4_HI {
125                3
126            } else {
127                4
128            }
129        } else if hi > OVERFLOW_MAX_7_HI {
130            if hi > OVERFLOW_MAX_6_HI {
131                5
132            } else {
133                6
134            }
135        } else if hi > OVERFLOW_MAX_8_HI {
136            7
137        } else {
138            8
139        };
140
141        // Double check what we've found won't overflow. Otherwise, we go one below.
142        if hi == POWER_OVERFLOW_VALUES[x - 1].data[2] && low64 > POWER_OVERFLOW_VALUES[x - 1].low64() {
143            x -= 1;
144        }
145
146        // Confirm we've actually resolved things
147        if x as i32 + scale < 0 {
148            None
149        } else {
150            Some(x)
151        }
152    }
153}
154
155// This is a table of the largest values that will not overflow when multiplied
156// by a given power as represented by the index.
157static POWER_OVERFLOW_VALUES: [Buf12; 8] = [
158    Buf12 {
159        data: [2576980377, 2576980377, 429496729],
160    },
161    Buf12 {
162        data: [687194767, 4123168604, 42949672],
163    },
164    Buf12 {
165        data: [2645699854, 1271310319, 4294967],
166    },
167    Buf12 {
168        data: [694066715, 3133608139, 429496],
169    },
170    Buf12 {
171        data: [2216890319, 2890341191, 42949],
172    },
173    Buf12 {
174        data: [2369172679, 4154504685, 4294],
175    },
176    Buf12 {
177        data: [4102387834, 2133437386, 429],
178    },
179    Buf12 {
180        data: [410238783, 4078814305, 42],
181    },
182];
183
184pub(super) struct Dec64 {
185    pub negative: bool,
186    pub scale: u32,
187    pub hi: u32,
188    pub low64: u64,
189}
190
191impl Dec64 {
192    pub(super) const fn new(d: &Decimal) -> Dec64 {
193        let m = d.mantissa_array3();
194        if m[1] == 0 {
195            Dec64 {
196                negative: d.is_sign_negative(),
197                scale: d.scale(),
198                hi: m[2],
199                low64: m[0] as u64,
200            }
201        } else {
202            Dec64 {
203                negative: d.is_sign_negative(),
204                scale: d.scale(),
205                hi: m[2],
206                low64: ((m[1] as u64) << 32) | (m[0] as u64),
207            }
208        }
209    }
210
211    #[inline(always)]
212    pub(super) const fn lo(&self) -> u32 {
213        self.low64 as u32
214    }
215    #[inline(always)]
216    pub(super) const fn mid(&self) -> u32 {
217        (self.low64 >> 32) as u32
218    }
219
220    #[inline(always)]
221    pub(super) const fn high64(&self) -> u64 {
222        (self.low64 >> 32) | ((self.hi as u64) << 32)
223    }
224
225    pub(super) const fn to_decimal(&self) -> Decimal {
226        Decimal::from_parts(
227            self.low64 as u32,
228            (self.low64 >> 32) as u32,
229            self.hi,
230            self.negative,
231            self.scale,
232        )
233    }
234}
235
236pub struct Buf16 {
237    pub data: [u32; 4],
238}
239
240impl Buf16 {
241    pub const fn zero() -> Self {
242        Buf16 { data: [0, 0, 0, 0] }
243    }
244
245    pub const fn low64(&self) -> u64 {
246        ((self.data[1] as u64) << 32) | (self.data[0] as u64)
247    }
248
249    pub fn set_low64(&mut self, value: u64) {
250        self.data[1] = (value >> 32) as u32;
251        self.data[0] = value as u32;
252    }
253
254    pub const fn mid64(&self) -> u64 {
255        ((self.data[2] as u64) << 32) | (self.data[1] as u64)
256    }
257
258    pub fn set_mid64(&mut self, value: u64) {
259        self.data[2] = (value >> 32) as u32;
260        self.data[1] = value as u32;
261    }
262
263    pub const fn high64(&self) -> u64 {
264        ((self.data[3] as u64) << 32) | (self.data[2] as u64)
265    }
266
267    pub fn set_high64(&mut self, value: u64) {
268        self.data[3] = (value >> 32) as u32;
269        self.data[2] = value as u32;
270    }
271}
272
273#[derive(Debug)]
274pub struct Buf24 {
275    pub data: [u32; 6],
276}
277
278impl Buf24 {
279    pub const fn zero() -> Self {
280        Buf24 {
281            data: [0, 0, 0, 0, 0, 0],
282        }
283    }
284
285    pub const fn low64(&self) -> u64 {
286        ((self.data[1] as u64) << 32) | (self.data[0] as u64)
287    }
288
289    pub fn set_low64(&mut self, value: u64) {
290        self.data[1] = (value >> 32) as u32;
291        self.data[0] = value as u32;
292    }
293
294    #[allow(dead_code)]
295    pub const fn mid64(&self) -> u64 {
296        ((self.data[3] as u64) << 32) | (self.data[2] as u64)
297    }
298
299    pub fn set_mid64(&mut self, value: u64) {
300        self.data[3] = (value >> 32) as u32;
301        self.data[2] = value as u32;
302    }
303
304    #[allow(dead_code)]
305    pub const fn high64(&self) -> u64 {
306        ((self.data[5] as u64) << 32) | (self.data[4] as u64)
307    }
308
309    pub fn set_high64(&mut self, value: u64) {
310        self.data[5] = (value >> 32) as u32;
311        self.data[4] = value as u32;
312    }
313
314    pub const fn upper_word(&self) -> usize {
315        if self.data[5] > 0 {
316            return 5;
317        }
318        if self.data[4] > 0 {
319            return 4;
320        }
321        if self.data[3] > 0 {
322            return 3;
323        }
324        if self.data[2] > 0 {
325            return 2;
326        }
327        if self.data[1] > 0 {
328            return 1;
329        }
330        0
331    }
332
333    // Attempt to rescale the number into 96 bits. If successful, the scale is returned wrapped
334    // in an Option. If it failed due to overflow, we return None.
335    // * `upper` - Index of last non-zero value in self.
336    // * `scale` - Current scale factor for this value.
337    pub fn rescale(&mut self, upper: usize, scale: u32) -> Option<u32> {
338        let mut scale = scale as i32;
339        let mut upper = upper;
340
341        // Determine a rescale target to start with
342        let mut rescale_target = 0i32;
343        if upper > 2 {
344            rescale_target = upper as i32 * 32 - 64 - 1;
345            rescale_target -= self.data[upper].leading_zeros() as i32;
346            rescale_target = ((rescale_target * 77) >> 8) + 1;
347            if rescale_target > scale {
348                return None;
349            }
350        }
351
352        // Make sure we scale enough to bring it into a valid range
353        if rescale_target < scale - MAX_SCALE_I32 {
354            rescale_target = scale - MAX_SCALE_I32;
355        }
356
357        if rescale_target > 0 {
358            // We're going to keep reducing by powers of 10. So, start by reducing the scale by
359            // that amount.
360            scale -= rescale_target;
361            let mut sticky = 0;
362            let mut remainder = 0;
363            loop {
364                sticky |= remainder;
365                let mut power = if rescale_target > 8 {
366                    POWERS_10[9]
367                } else {
368                    POWERS_10[rescale_target as usize]
369                };
370
371                let high = self.data[upper];
372                let high_quotient = high / power;
373                remainder = high - high_quotient * power;
374
375                for item in self.data.iter_mut().rev().skip(6 - upper) {
376                    let num = (*item as u64).wrapping_add((remainder as u64) << 32);
377                    *item = (num / power as u64) as u32;
378                    remainder = (num as u32).wrapping_sub(item.wrapping_mul(power));
379                }
380
381                self.data[upper] = high_quotient;
382
383                // If the high quotient was zero then decrease the upper bound
384                if high_quotient == 0 && upper > 0 {
385                    upper -= 1;
386                }
387                if rescale_target > MAX_I32_SCALE {
388                    // Scale some more
389                    rescale_target -= MAX_I32_SCALE;
390                    continue;
391                }
392
393                // If we fit into 96 bits then we've scaled enough. Otherwise, scale once more.
394                if upper > 2 {
395                    if scale == 0 {
396                        return None;
397                    }
398                    // Equivalent to scaling down by 10
399                    rescale_target = 1;
400                    scale -= 1;
401                    continue;
402                }
403
404                // Round the final result.
405                power >>= 1;
406                let carried = if power <= remainder {
407                    // If we're less than half then we're fine. Otherwise, we round if odd or if the
408                    // sticky bit is set.
409                    if power < remainder || ((self.data[0] & 1) | sticky) != 0 {
410                        // Round up
411                        self.data[0] = self.data[0].wrapping_add(1);
412                        // Check if we carried
413                        self.data[0] == 0
414                    } else {
415                        false
416                    }
417                } else {
418                    false
419                };
420
421                // If we carried then propagate through the portions
422                if carried {
423                    let mut pos = 0;
424                    for (index, value) in self.data.iter_mut().enumerate().skip(1) {
425                        pos = index;
426                        *value = value.wrapping_add(1);
427                        if *value != 0 {
428                            break;
429                        }
430                    }
431
432                    // If we ended up rounding over the 96 bits then we'll try to rescale down (again)
433                    if pos > 2 {
434                        // Nothing to scale down from will cause overflow
435                        if scale == 0 {
436                            return None;
437                        }
438
439                        // Loop back around using scale of 10.
440                        // Reset the sticky bit and remainder before looping.
441                        upper = pos;
442                        sticky = 0;
443                        remainder = 0;
444                        rescale_target = 1;
445                        scale -= 1;
446                        continue;
447                    }
448                }
449                break;
450            }
451        }
452
453        Some(scale as u32)
454    }
455}