macro_rules! round_with_mode_native {
($n:expr, $m:expr, $mode:expr) => {{
let n = $n;
let m = $m;
let mode = $mode;
let q = n / m;
let r = n % m;
if r == 0 {
q
} else {
let abs_r = if r < 0 { -r } else { r };
let abs_m = if m < 0 { -m } else { m };
let comp = abs_m - abs_r;
let cmp_r = abs_r.cmp(&comp);
let q_is_odd = (q & 1) != 0;
let result_positive = (n < 0) == (m < 0);
if $crate::support::rounding::should_bump(mode, cmp_r, q_is_odd, result_positive) {
if result_positive {
q + 1
} else {
q - 1
}
} else {
q
}
}
}};
}
pub(crate) use round_with_mode_native;
#[inline(always)]
pub(crate) fn i128_divrem_by_u64_with_mode(
n: i128,
m_mag: u64,
mode: crate::support::rounding::RoundingMode,
) -> i128 {
debug_assert!(m_mag != 0, "i128_divrem_by_u64_with_mode: m_mag = 0");
let n_neg = n < 0;
let un = n.unsigned_abs();
let (q_mag, r_mag) = {
let hi = (un >> 64) as u64;
let lo = un as u64;
if hi == 0 {
let q = lo / m_mag;
let r = lo % m_mag;
(q as u128, r)
} else {
let q_hi = hi / m_mag;
let r_hi = hi % m_mag;
let cur = ((r_hi as u128) << 64) | (lo as u128);
let q_lo_u128 = cur / (m_mag as u128);
let r = cur - q_lo_u128 * (m_mag as u128);
let q = ((q_hi as u128) << 64) | (q_lo_u128 & u128::from(u64::MAX));
(q, r as u64)
}
};
if r_mag == 0 {
return if n_neg { -(q_mag as i128) } else { q_mag as i128 };
}
let abs_r = r_mag as u128;
let abs_m = m_mag as u128;
let comp = abs_m - abs_r;
let cmp_r = abs_r.cmp(&comp);
let q_is_odd = (q_mag & 1) != 0;
let result_positive = !n_neg;
let bump = crate::support::rounding::should_bump(mode, cmp_r, q_is_odd, result_positive);
let bumped_mag = if bump { q_mag + 1 } else { q_mag };
if n_neg {
-(bumped_mag as i128)
} else {
bumped_mag as i128
}
}
#[cfg(any(feature = "d76", feature = "d153", feature = "d307", feature = "wide", feature = "x-wide"))]
macro_rules! round_with_mode_wide {
($n:expr, $m:expr, $W:ty, $mode:expr) => {{
let n = $n;
let m = $m;
let mode = $mode;
let (q, r) = n.div_rem(m);
let zero = <$W>::from_i128(0);
if r == zero {
q
} else {
let one = <$W>::from_i128(1);
let abs_r = if r < zero { -r } else { r };
let abs_m = if m < zero { -m } else { m };
let comp = abs_m - abs_r;
let cmp_r = abs_r.cmp(&comp);
let q_is_odd = {
let two = <$W>::from_i128(2);
(q % two) != zero
};
let result_positive = (n < zero) == (m < zero);
if $crate::support::rounding::should_bump(mode, cmp_r, q_is_odd, result_positive) {
if result_positive {
q + one
} else {
q - one
}
} else {
q
}
}
}};
}
#[cfg(any(feature = "d76", feature = "d153", feature = "d307", feature = "wide", feature = "x-wide"))]
pub(crate) use round_with_mode_wide;
macro_rules! decl_decimal_arithmetic {
(wide $Type:ident, $Storage:ty, $Wider:ty) => {
$crate::macros::arithmetic::decl_decimal_arithmetic!(@common $Type, $Storage);
impl<const SCALE: u32> ::core::ops::Mul for $Type<SCALE> {
type Output = Self;
#[inline]
fn mul(self, rhs: Self) -> Self {
self.mul_with(rhs, $crate::support::rounding::DEFAULT_ROUNDING_MODE)
}
}
impl<const SCALE: u32> ::core::ops::MulAssign for $Type<SCALE> {
#[inline]
fn mul_assign(&mut self, rhs: Self) {
*self = *self * rhs;
}
}
impl<const SCALE: u32> ::core::ops::Div for $Type<SCALE> {
type Output = Self;
#[inline]
fn div(self, rhs: Self) -> Self {
self.div_with(rhs, $crate::support::rounding::DEFAULT_ROUNDING_MODE)
}
}
impl<const SCALE: u32> $Type<SCALE> {
#[inline]
pub fn mul_with(self, rhs: Self, mode: $crate::support::rounding::RoundingMode) -> Self {
let lz_a = self.0.leading_zeros();
let lz_b = rhs.0.leading_zeros();
if lz_a + lz_b > <$Storage>::BITS {
let n: $Storage = self.0.wrapping_mul(rhs.0);
let scaled = if SCALE == 0 {
n
} else if SCALE <= 38 {
$crate::algos::mg_divide::div_wide_pow10_with::<$Storage>(n, SCALE, mode)
} else {
$crate::algos::mg_divide::div_wide_pow10_chain_with::<$Storage>(n, SCALE, mode)
};
return Self(scaled);
}
let n: $Wider = self.0.widen_mul::<$Wider>(rhs.0);
let scaled = if SCALE == 0 {
n
} else if SCALE <= 38 {
$crate::algos::mg_divide::div_wide_pow10_with::<$Wider>(n, SCALE, mode)
} else {
$crate::algos::mg_divide::div_wide_pow10_chain_with::<$Wider>(n, SCALE, mode)
};
Self(scaled.resize::<$Storage>())
}
#[inline]
pub fn div_with(self, rhs: Self, mode: $crate::support::rounding::RoundingMode) -> Self {
let mult: $Storage = $Type::<SCALE>::multiplier();
let lz_n = self.0.leading_zeros();
let lz_m = mult.leading_zeros();
if lz_n + lz_m > <$Storage>::BITS {
let n: $Storage = self.0.wrapping_mul(mult);
let result =
$crate::macros::arithmetic::round_with_mode_wide!(n, rhs.0, $Storage, mode);
return Self(result);
}
let b: $Wider = rhs.0.resize::<$Wider>();
let n: $Wider = self.0.widen_mul::<$Wider>($Type::<SCALE>::multiplier());
let result =
$crate::macros::arithmetic::round_with_mode_wide!(n, b, $Wider, mode);
Self(result.resize::<$Storage>())
}
}
impl<const SCALE: u32> ::core::ops::DivAssign for $Type<SCALE> {
#[inline]
fn div_assign(&mut self, rhs: Self) {
*self = *self / rhs;
}
}
};
($Type:ident, $Storage:ty, i64) => {
$crate::macros::arithmetic::decl_decimal_arithmetic!(@common $Type, $Storage);
$crate::macros::arithmetic::decl_decimal_arithmetic!(@native_i64_wider $Type, $Storage);
};
($Type:ident, $Storage:ty, i128) => {
$crate::macros::arithmetic::decl_decimal_arithmetic!(@common $Type, $Storage);
$crate::macros::arithmetic::decl_decimal_arithmetic!(@native_i128_wider $Type, $Storage);
};
(@native_i64_wider $Type:ident, $Storage:ty) => {
impl<const SCALE: u32> ::core::ops::Mul for $Type<SCALE> {
type Output = Self;
#[inline]
fn mul(self, rhs: Self) -> Self {
self.mul_with(rhs, $crate::support::rounding::DEFAULT_ROUNDING_MODE)
}
}
impl<const SCALE: u32> ::core::ops::MulAssign for $Type<SCALE> {
#[inline]
fn mul_assign(&mut self, rhs: Self) {
*self = *self * rhs;
}
}
impl<const SCALE: u32> ::core::ops::Div for $Type<SCALE> {
type Output = Self;
#[inline]
fn div(self, rhs: Self) -> Self {
self.div_with(rhs, $crate::support::rounding::DEFAULT_ROUNDING_MODE)
}
}
impl<const SCALE: u32> $Type<SCALE> {
#[inline]
pub fn mul_with(self, rhs: Self, mode: $crate::support::rounding::RoundingMode) -> Self {
let a = self.0 as i64;
let b = rhs.0 as i64;
let m = (10i64).pow(SCALE);
let n = a * b;
let scaled =
$crate::macros::arithmetic::round_with_mode_native!(n, m, mode);
Self(scaled as $Storage)
}
#[inline]
pub fn div_with(self, rhs: Self, mode: $crate::support::rounding::RoundingMode) -> Self {
let a = self.0 as i64;
let b = rhs.0 as i64;
let m = (10i64).pow(SCALE);
let n = a * m;
let result =
$crate::macros::arithmetic::round_with_mode_native!(n, b, mode);
Self(result as $Storage)
}
}
impl<const SCALE: u32> ::core::ops::DivAssign for $Type<SCALE> {
#[inline]
fn div_assign(&mut self, rhs: Self) {
*self = *self / rhs;
}
}
};
(@native_i128_wider $Type:ident, $Storage:ty) => {
impl<const SCALE: u32> ::core::ops::Mul for $Type<SCALE> {
type Output = Self;
#[inline]
fn mul(self, rhs: Self) -> Self {
self.mul_with(rhs, $crate::support::rounding::DEFAULT_ROUNDING_MODE)
}
}
impl<const SCALE: u32> ::core::ops::MulAssign for $Type<SCALE> {
#[inline]
fn mul_assign(&mut self, rhs: Self) {
*self = *self * rhs;
}
}
impl<const SCALE: u32> ::core::ops::Div for $Type<SCALE> {
type Output = Self;
#[inline]
fn div(self, rhs: Self) -> Self {
self.div_with(rhs, $crate::support::rounding::DEFAULT_ROUNDING_MODE)
}
}
impl<const SCALE: u32> $Type<SCALE> {
#[inline]
pub fn mul_with(self, rhs: Self, mode: $crate::support::rounding::RoundingMode) -> Self {
let a = self.0 as i128;
let b = rhs.0 as i128;
let n = a * b;
let scaled: i128 = if SCALE == 0 {
n
} else {
let m_mag: u64 = (10u64).pow(SCALE);
$crate::macros::arithmetic::i128_divrem_by_u64_with_mode(n, m_mag, mode)
};
Self(scaled as $Storage)
}
#[inline]
pub fn div_with(self, rhs: Self, mode: $crate::support::rounding::RoundingMode) -> Self {
let a = self.0 as i128;
let b = rhs.0 as i128;
let m = (10i128).pow(SCALE);
let n = a * m;
let b_neg = b < 0;
let b_mag: u64 = if b_neg {
(rhs.0 as i64).unsigned_abs()
} else {
rhs.0 as u64
};
let q =
$crate::macros::arithmetic::i128_divrem_by_u64_with_mode(n, b_mag, mode);
let result = if b_neg { -q } else { q };
Self(result as $Storage)
}
}
impl<const SCALE: u32> ::core::ops::DivAssign for $Type<SCALE> {
#[inline]
fn div_assign(&mut self, rhs: Self) {
*self = *self / rhs;
}
}
};
(@common $Type:ident, $Storage:ty) => {
impl<const SCALE: u32> ::core::ops::Add for $Type<SCALE> {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self {
Self(self.0 + rhs.0)
}
}
impl<const SCALE: u32> ::core::ops::AddAssign for $Type<SCALE> {
#[inline]
fn add_assign(&mut self, rhs: Self) {
self.0 = self.0 + rhs.0;
}
}
impl<const SCALE: u32> ::core::ops::Sub for $Type<SCALE> {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self {
Self(self.0 - rhs.0)
}
}
impl<const SCALE: u32> ::core::ops::SubAssign for $Type<SCALE> {
#[inline]
fn sub_assign(&mut self, rhs: Self) {
self.0 = self.0 - rhs.0;
}
}
impl<const SCALE: u32> ::core::ops::Neg for $Type<SCALE> {
type Output = Self;
#[inline]
fn neg(self) -> Self {
Self(-self.0)
}
}
impl<const SCALE: u32> ::core::ops::Rem for $Type<SCALE> {
type Output = Self;
#[inline]
fn rem(self, rhs: Self) -> Self {
Self(self.0 % rhs.0)
}
}
impl<const SCALE: u32> ::core::ops::RemAssign for $Type<SCALE> {
#[inline]
fn rem_assign(&mut self, rhs: Self) {
self.0 = self.0 % rhs.0;
}
}
};
}
pub(crate) use decl_decimal_arithmetic;