macro_rules! decl_decimal_rescale {
(wide $Type:ident, $Storage:ty) => {
impl<const SCALE: u32> $Type<SCALE> {
#[inline]
#[must_use]
pub fn rescale<const TARGET_SCALE: u32>(self) -> $Type<TARGET_SCALE> {
self.rescale_with::<TARGET_SCALE>($crate::support::rounding::DEFAULT_ROUNDING_MODE)
}
#[inline]
#[must_use]
pub fn with_scale<const TARGET_SCALE: u32>(self) -> $Type<TARGET_SCALE> {
self.rescale::<TARGET_SCALE>()
}
#[inline]
#[must_use]
pub fn rescale_with<const TARGET_SCALE: u32>(
self,
mode: $crate::support::rounding::RoundingMode,
) -> $Type<TARGET_SCALE> {
if TARGET_SCALE == SCALE {
return $Type::<TARGET_SCALE>::from_bits(self.0);
}
let ten = <$Storage>::from_str_radix("10", 10)
.expect("wide decimal: invalid base-10 literal");
let one = <$Storage>::from_str_radix("1", 10)
.expect("wide decimal: invalid base-10 literal");
let zero = <$Storage>::from_str_radix("0", 10)
.expect("wide decimal: invalid base-10 literal");
if TARGET_SCALE > SCALE {
let shift = TARGET_SCALE - SCALE;
let multiplier = ten.pow(shift);
let result = match self.0.checked_mul(multiplier) {
Some(v) => v,
None => panic!(concat!(stringify!($Type), "::rescale: scale-up overflow")),
};
return $Type::<TARGET_SCALE>::from_bits(result);
}
let shift = SCALE - TARGET_SCALE;
let divisor = ten.pow(shift);
let raw = self.0;
let quotient = raw / divisor;
let remainder = raw % divisor;
if remainder == zero {
return $Type::<TARGET_SCALE>::from_bits(quotient);
}
let abs_rem = remainder.unsigned_abs();
let half = divisor.unsigned_abs() >> 1;
let non_negative = !raw.is_negative();
let bits = match mode {
$crate::support::rounding::RoundingMode::HalfToEven => {
if abs_rem < half {
quotient
} else if abs_rem > half {
if non_negative {
quotient + one
} else {
quotient - one
}
} else if !quotient.bit(0) {
quotient
} else if non_negative {
quotient + one
} else {
quotient - one
}
}
$crate::support::rounding::RoundingMode::HalfAwayFromZero => {
if abs_rem < half {
quotient
} else if non_negative {
quotient + one
} else {
quotient - one
}
}
$crate::support::rounding::RoundingMode::HalfTowardZero => {
if abs_rem > half {
if non_negative {
quotient + one
} else {
quotient - one
}
} else {
quotient
}
}
$crate::support::rounding::RoundingMode::Trunc => quotient,
$crate::support::rounding::RoundingMode::Floor => {
if non_negative {
quotient
} else {
quotient - one
}
}
$crate::support::rounding::RoundingMode::Ceiling => {
if non_negative {
quotient + one
} else {
quotient
}
}
};
$Type::<TARGET_SCALE>::from_bits(bits)
}
}
};
}
pub(crate) use decl_decimal_rescale;