use crate::constants::{MAX_I32_SCALE, MAX_PRECISION_I32, POWERS_10}; use crate::Decimal; #[derive(Debug)] pub struct Buf12 { pub data: [u32; 3], } impl Buf12 { pub(super) const fn from_dec64(value: &Dec64) -> Self { Buf12 { data: [value.low64 as u32, (value.low64 >> 32) as u32, value.hi], } } pub(super) const fn from_decimal(value: &Decimal) -> Self { Buf12 { data: value.mantissa_array3(), } } #[inline(always)] pub const fn lo(&self) -> u32 { self.data[0] } #[inline(always)] pub const fn mid(&self) -> u32 { self.data[1] } #[inline(always)] pub const fn hi(&self) -> u32 { self.data[2] } #[inline(always)] pub fn set_lo(&mut self, value: u32) { self.data[0] = value; } #[inline(always)] pub fn set_mid(&mut self, value: u32) { self.data[1] = value; } #[inline(always)] pub fn set_hi(&mut self, value: u32) { self.data[2] = value; } #[inline(always)] pub const fn low64(&self) -> u64 { ((self.data[1] as u64) << 32) | (self.data[0] as u64) } #[inline(always)] pub fn set_low64(&mut self, value: u64) { self.data[1] = (value >> 32) as u32; self.data[0] = value as u32; } #[inline(always)] pub const fn high64(&self) -> u64 { ((self.data[2] as u64) << 32) | (self.data[1] as u64) } #[inline(always)] pub fn set_high64(&mut self, value: u64) { self.data[2] = (value >> 32) as u32; self.data[1] = value as u32; } // Determine the maximum value of x that ensures that the quotient when scaled up by 10^x // still fits in 96 bits. Ultimately, we want to make scale positive - if we can't then // we're going to overflow. Because x is ultimately used to lookup inside the POWERS array, it // must be a valid value 0 <= x <= 9 pub fn find_scale(&self, scale: i32) -> Option { const OVERFLOW_MAX_9_HI: u32 = 4; const OVERFLOW_MAX_8_HI: u32 = 42; const OVERFLOW_MAX_7_HI: u32 = 429; const OVERFLOW_MAX_6_HI: u32 = 4294; const OVERFLOW_MAX_5_HI: u32 = 42949; const OVERFLOW_MAX_4_HI: u32 = 429496; const OVERFLOW_MAX_3_HI: u32 = 4294967; const OVERFLOW_MAX_2_HI: u32 = 42949672; const OVERFLOW_MAX_1_HI: u32 = 429496729; const OVERFLOW_MAX_9_LOW64: u64 = 5441186219426131129; let hi = self.data[2]; let low64 = self.low64(); let mut x = 0usize; // Quick check to stop us from trying to scale any more. // if hi > OVERFLOW_MAX_1_HI { // If it's less than 0, which it probably is - overflow. We can't do anything. if scale < 0 { return None; } return Some(x); } if scale > MAX_PRECISION_I32 - 9 { // We can't scale by 10^9 without exceeding the max scale factor. // Instead, we'll try to scale by the most that we can and see if that works. // This is safe to do due to the check above. e.g. scale > 19 in the above, so it will // evaluate to 9 or less below. x = (MAX_PRECISION_I32 - scale) as usize; if hi < POWER_OVERFLOW_VALUES[x - 1].data[2] { if x as i32 + scale < 0 { // We still overflow return None; } return Some(x); } } else if hi < OVERFLOW_MAX_9_HI || hi == OVERFLOW_MAX_9_HI && low64 <= OVERFLOW_MAX_9_LOW64 { return Some(9); } // Do a binary search to find a power to scale by that is less than 9 x = if hi > OVERFLOW_MAX_5_HI { if hi > OVERFLOW_MAX_3_HI { if hi > OVERFLOW_MAX_2_HI { 1 } else { 2 } } else if hi > OVERFLOW_MAX_4_HI { 3 } else { 4 } } else if hi > OVERFLOW_MAX_7_HI { if hi > OVERFLOW_MAX_6_HI { 5 } else { 6 } } else if hi > OVERFLOW_MAX_8_HI { 7 } else { 8 }; // Double check what we've found won't overflow. Otherwise, we go one below. if hi == POWER_OVERFLOW_VALUES[x - 1].data[2] && low64 > POWER_OVERFLOW_VALUES[x - 1].low64() { x -= 1; } // Confirm we've actually resolved things if x as i32 + scale < 0 { None } else { Some(x) } } } // This is a table of the largest values that will not overflow when multiplied // by a given power as represented by the index. static POWER_OVERFLOW_VALUES: [Buf12; 8] = [ Buf12 { data: [2576980377, 2576980377, 429496729], }, Buf12 { data: [687194767, 4123168604, 42949672], }, Buf12 { data: [2645699854, 1271310319, 4294967], }, Buf12 { data: [694066715, 3133608139, 429496], }, Buf12 { data: [2216890319, 2890341191, 42949], }, Buf12 { data: [2369172679, 4154504685, 4294], }, Buf12 { data: [4102387834, 2133437386, 429], }, Buf12 { data: [410238783, 4078814305, 42], }, ]; pub(super) struct Dec64 { pub negative: bool, pub scale: u32, pub hi: u32, pub low64: u64, } impl Dec64 { pub(super) const fn new(d: &Decimal) -> Dec64 { let m = d.mantissa_array3(); if m[1] == 0 { Dec64 { negative: d.is_sign_negative(), scale: d.scale(), hi: m[2], low64: m[0] as u64, } } else { Dec64 { negative: d.is_sign_negative(), scale: d.scale(), hi: m[2], low64: ((m[1] as u64) << 32) | (m[0] as u64), } } } #[inline(always)] pub(super) const fn lo(&self) -> u32 { self.low64 as u32 } #[inline(always)] pub(super) const fn mid(&self) -> u32 { (self.low64 >> 32) as u32 } #[inline(always)] pub(super) const fn high64(&self) -> u64 { (self.low64 >> 32) | ((self.hi as u64) << 32) } pub(super) const fn to_decimal(&self) -> Decimal { Decimal::from_parts( self.low64 as u32, (self.low64 >> 32) as u32, self.hi, self.negative, self.scale, ) } } pub struct Buf16 { pub data: [u32; 4], } impl Buf16 { pub const fn zero() -> Self { Buf16 { data: [0, 0, 0, 0] } } pub const fn low64(&self) -> u64 { ((self.data[1] as u64) << 32) | (self.data[0] as u64) } pub fn set_low64(&mut self, value: u64) { self.data[1] = (value >> 32) as u32; self.data[0] = value as u32; } pub const fn mid64(&self) -> u64 { ((self.data[2] as u64) << 32) | (self.data[1] as u64) } pub fn set_mid64(&mut self, value: u64) { self.data[2] = (value >> 32) as u32; self.data[1] = value as u32; } pub const fn high64(&self) -> u64 { ((self.data[3] as u64) << 32) | (self.data[2] as u64) } pub fn set_high64(&mut self, value: u64) { self.data[3] = (value >> 32) as u32; self.data[2] = value as u32; } } #[derive(Debug)] pub struct Buf24 { pub data: [u32; 6], } impl Buf24 { pub const fn zero() -> Self { Buf24 { data: [0, 0, 0, 0, 0, 0], } } pub const fn low64(&self) -> u64 { ((self.data[1] as u64) << 32) | (self.data[0] as u64) } pub fn set_low64(&mut self, value: u64) { self.data[1] = (value >> 32) as u32; self.data[0] = value as u32; } #[allow(dead_code)] pub const fn mid64(&self) -> u64 { ((self.data[3] as u64) << 32) | (self.data[2] as u64) } pub fn set_mid64(&mut self, value: u64) { self.data[3] = (value >> 32) as u32; self.data[2] = value as u32; } #[allow(dead_code)] pub const fn high64(&self) -> u64 { ((self.data[5] as u64) << 32) | (self.data[4] as u64) } pub fn set_high64(&mut self, value: u64) { self.data[5] = (value >> 32) as u32; self.data[4] = value as u32; } pub const fn upper_word(&self) -> usize { if self.data[5] > 0 { return 5; } if self.data[4] > 0 { return 4; } if self.data[3] > 0 { return 3; } if self.data[2] > 0 { return 2; } if self.data[1] > 0 { return 1; } 0 } // Attempt to rescale the number into 96 bits. If successful, the scale is returned wrapped // in an Option. If it failed due to overflow, we return None. // * `upper` - Index of last non-zero value in self. // * `scale` - Current scale factor for this value. pub fn rescale(&mut self, upper: usize, scale: u32) -> Option { let mut scale = scale as i32; let mut upper = upper; // Determine a rescale target to start with let mut rescale_target = 0i32; if upper > 2 { rescale_target = upper as i32 * 32 - 64 - 1; rescale_target -= self.data[upper].leading_zeros() as i32; rescale_target = ((rescale_target * 77) >> 8) + 1; if rescale_target > scale { return None; } } // Make sure we scale enough to bring it into a valid range if rescale_target < scale - MAX_PRECISION_I32 { rescale_target = scale - MAX_PRECISION_I32; } if rescale_target > 0 { // We're going to keep reducing by powers of 10. So, start by reducing the scale by // that amount. scale -= rescale_target; let mut sticky = 0; let mut remainder = 0; loop { sticky |= remainder; let mut power = if rescale_target > 8 { POWERS_10[9] } else { POWERS_10[rescale_target as usize] }; let high = self.data[upper]; let high_quotient = high / power; remainder = high - high_quotient * power; for item in self.data.iter_mut().rev().skip(6 - upper) { let num = (*item as u64).wrapping_add((remainder as u64) << 32); *item = (num / power as u64) as u32; remainder = (num as u32).wrapping_sub(item.wrapping_mul(power)); } self.data[upper] = high_quotient; // If the high quotient was zero then decrease the upper bound if high_quotient == 0 && upper > 0 { upper -= 1; } if rescale_target > MAX_I32_SCALE { // Scale some more rescale_target -= MAX_I32_SCALE; continue; } // If we fit into 96 bits then we've scaled enough. Otherwise, scale once more. if upper > 2 { if scale == 0 { return None; } // Equivalent to scaling down by 10 rescale_target = 1; scale -= 1; continue; } // Round the final result. power >>= 1; let carried = if power <= remainder { // If we're less than half then we're fine. Otherwise, we round if odd or if the // sticky bit is set. if power < remainder || ((self.data[0] & 1) | sticky) != 0 { // Round up self.data[0] = self.data[0].wrapping_add(1); // Check if we carried self.data[0] == 0 } else { false } } else { false }; // If we carried then propagate through the portions if carried { let mut pos = 0; for (index, value) in self.data.iter_mut().enumerate().skip(1) { pos = index; *value = value.wrapping_add(1); if *value != 0 { break; } } // If we ended up rounding over the 96 bits then we'll try to rescale down (again) if pos > 2 { // Nothing to scale down from will cause overflow if scale == 0 { return None; } // Loop back around using scale of 10. // Reset the sticky bit and remainder before looping. upper = pos; sticky = 0; remainder = 0; rescale_target = 1; scale -= 1; continue; } } break; } } Some(scale as u32) } }