use crate::Decimal;
use crate::error::{DivError, OverflowError};
macro_rules! impl_decimal_arithmetic {
($backing:ty) => {
impl<const D: u8> Decimal<$backing, D> {
#[inline(always)]
pub const fn abs(self) -> Self {
Self {
value: self.value.abs(),
}
}
#[inline(always)]
pub const fn checked_add(self, rhs: Self) -> Option<Self> {
match self.value.checked_add(rhs.value) {
Some(v) => Some(Self { value: v }),
None => None,
}
}
#[inline(always)]
pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
match self.value.checked_sub(rhs.value) {
Some(v) => Some(Self { value: v }),
None => None,
}
}
#[inline(always)]
pub const fn checked_neg(self) -> Option<Self> {
match self.value.checked_neg() {
Some(v) => Some(Self { value: v }),
None => None,
}
}
#[inline(always)]
pub const fn checked_abs(self) -> Option<Self> {
if self.value >= 0 {
Some(self)
} else {
self.checked_neg()
}
}
#[inline(always)]
pub const fn saturating_add(self, rhs: Self) -> Self {
Self {
value: self.value.saturating_add(rhs.value),
}
}
#[inline(always)]
pub const fn saturating_sub(self, rhs: Self) -> Self {
Self {
value: self.value.saturating_sub(rhs.value),
}
}
#[inline(always)]
pub const fn saturating_neg(self) -> Self {
Self {
value: self.value.saturating_neg(),
}
}
#[inline(always)]
pub const fn saturating_abs(self) -> Self {
Self {
value: self.value.saturating_abs(),
}
}
#[inline(always)]
pub const fn wrapping_add(self, rhs: Self) -> Self {
Self {
value: self.value.wrapping_add(rhs.value),
}
}
#[inline(always)]
pub const fn wrapping_sub(self, rhs: Self) -> Self {
Self {
value: self.value.wrapping_sub(rhs.value),
}
}
#[inline(always)]
pub const fn wrapping_neg(self) -> Self {
Self {
value: self.value.wrapping_neg(),
}
}
#[inline(always)]
pub const fn wrapping_abs(self) -> Self {
Self {
value: self.value.wrapping_abs(),
}
}
#[inline(always)]
pub const fn try_add(self, rhs: Self) -> Result<Self, OverflowError> {
match self.checked_add(rhs) {
Some(v) => Ok(v),
None => Err(OverflowError),
}
}
#[inline(always)]
pub const fn try_sub(self, rhs: Self) -> Result<Self, OverflowError> {
match self.checked_sub(rhs) {
Some(v) => Ok(v),
None => Err(OverflowError),
}
}
#[inline(always)]
pub const fn try_neg(self) -> Result<Self, OverflowError> {
match self.checked_neg() {
Some(v) => Ok(v),
None => Err(OverflowError),
}
}
#[inline(always)]
pub const fn try_abs(self) -> Result<Self, OverflowError> {
match self.checked_abs() {
Some(v) => Ok(v),
None => Err(OverflowError),
}
}
}
};
}
impl_decimal_arithmetic!(i32);
impl_decimal_arithmetic!(i64);
impl_decimal_arithmetic!(i128);
impl<const D: u8> Decimal<i32, D> {
#[inline(always)]
pub const fn checked_mul(self, rhs: Self) -> Option<Self> {
let product = (self.value as i64) * (rhs.value as i64);
let result = product / (Self::SCALE as i64);
if result > i32::MAX as i64 || result < i32::MIN as i64 {
None
} else {
Some(Self {
value: result as i32,
})
}
}
#[inline(always)]
pub const fn checked_div(self, rhs: Self) -> Option<Self> {
if rhs.value == 0 {
return None;
}
let a = self.value as i64;
let b = rhs.value as i64;
let result = (a * Self::SCALE as i64) / b;
if result > i32::MAX as i64 || result < i32::MIN as i64 {
None
} else {
Some(Self {
value: result as i32,
})
}
}
#[inline(always)]
pub const fn saturating_mul(self, rhs: Self) -> Self {
let product = (self.value as i64) * (rhs.value as i64);
let result = product / (Self::SCALE as i64);
if result > i32::MAX as i64 {
Self::MAX
} else if result < i32::MIN as i64 {
Self::MIN
} else {
Self {
value: result as i32,
}
}
}
#[inline(always)]
pub const fn wrapping_mul(self, rhs: Self) -> Self {
let product = (self.value as i64) * (rhs.value as i64);
Self {
value: (product / (Self::SCALE as i64)) as i32,
}
}
#[inline(always)]
pub const fn saturating_div(self, rhs: Self) -> Self {
assert!(rhs.value != 0, "division by zero");
match self.checked_div(rhs) {
Some(v) => v,
None => {
if (self.value > 0) == (rhs.value > 0) {
Self::MAX
} else {
Self::MIN
}
}
}
}
#[inline(always)]
pub const fn wrapping_div(self, rhs: Self) -> Self {
assert!(rhs.value != 0, "division by zero");
let a = self.value as i64;
let b = rhs.value as i64;
Self {
value: ((a * Self::SCALE as i64) / b) as i32,
}
}
#[inline(always)]
pub const fn mul_int(self, rhs: i32) -> Option<Self> {
match self.value.checked_mul(rhs) {
Some(v) => Some(Self { value: v }),
None => None,
}
}
#[inline(always)]
pub const fn mul_add(self, mul: Self, add: Self) -> Option<Self> {
let product = (self.value as i64) * (mul.value as i64);
let rescaled = product / (Self::SCALE as i64);
let result = rescaled + (add.value as i64);
if result > i32::MAX as i64 || result < i32::MIN as i64 {
None
} else {
Some(Self {
value: result as i32,
})
}
}
#[inline(always)]
pub const fn try_mul(self, rhs: Self) -> Result<Self, OverflowError> {
match self.checked_mul(rhs) {
Some(v) => Ok(v),
None => Err(OverflowError),
}
}
#[inline(always)]
pub const fn try_div(self, rhs: Self) -> Result<Self, DivError> {
if rhs.value == 0 {
return Err(DivError::DivisionByZero);
}
match self.checked_div(rhs) {
Some(v) => Ok(v),
None => Err(DivError::Overflow),
}
}
}
use crate::div_by_scale;
impl<const D: u8> Decimal<i64, D> {
const USE_CHUNKED: bool = (Self::SCALE as u64) < div_by_scale::CHUNK_THRESHOLD;
#[inline(always)]
const fn div_product_by_scale(product: i128) -> Option<i64> {
div_by_scale::div_i128_by_scale(
product,
Self::SCALE as i128,
Self::SCALE as u64,
Self::USE_CHUNKED,
)
}
#[inline(always)]
const fn div_product_by_scale_wrapping(product: i128) -> i64 {
div_by_scale::div_i128_by_scale_wrapping(
product,
Self::SCALE as i128,
Self::SCALE as u64,
Self::USE_CHUNKED,
)
}
#[inline(always)]
pub const fn checked_mul(self, rhs: Self) -> Option<Self> {
let a = self.value as i128;
let b = rhs.value as i128;
let Some(product) = a.checked_mul(b) else {
return None;
};
match Self::div_product_by_scale(product) {
Some(result) => Some(Self { value: result }),
None => None,
}
}
#[inline(always)]
pub const fn checked_div(self, rhs: Self) -> Option<Self> {
if rhs.value == 0 {
return None;
}
let a = self.value as i128;
let b = rhs.value as i128;
let result = (a * Self::SCALE as i128) / b;
if result > i64::MAX as i128 || result < i64::MIN as i128 {
None
} else {
Some(Self {
value: result as i64,
})
}
}
#[inline(always)]
pub const fn saturating_mul(self, rhs: Self) -> Self {
let product = (self.value as i128) * (rhs.value as i128);
match Self::div_product_by_scale(product) {
Some(result) => Self { value: result },
None => {
if product > 0 {
Self::MAX
} else {
Self::MIN
}
}
}
}
#[inline(always)]
pub const fn wrapping_mul(self, rhs: Self) -> Self {
let product = (self.value as i128).wrapping_mul(rhs.value as i128);
Self {
value: Self::div_product_by_scale_wrapping(product),
}
}
#[inline(always)]
pub const fn saturating_div(self, rhs: Self) -> Self {
assert!(rhs.value != 0, "division by zero");
match self.checked_div(rhs) {
Some(v) => v,
None => {
if (self.value > 0) == (rhs.value > 0) {
Self::MAX
} else {
Self::MIN
}
}
}
}
#[inline(always)]
pub const fn wrapping_div(self, rhs: Self) -> Self {
assert!(rhs.value != 0, "division by zero");
let a = self.value as i128;
let b = rhs.value as i128;
Self {
value: ((a * Self::SCALE as i128) / b) as i64,
}
}
#[inline(always)]
pub const fn mul_int(self, rhs: i64) -> Option<Self> {
match self.value.checked_mul(rhs) {
Some(v) => Some(Self { value: v }),
None => None,
}
}
#[inline(always)]
pub const fn mul_add(self, mul: Self, add: Self) -> Option<Self> {
let a = self.value as i128;
let b = mul.value as i128;
let Some(product) = a.checked_mul(b) else {
return None;
};
let Some(rescaled) = Self::div_product_by_scale(product) else {
return None;
};
let rescaled = rescaled as i128;
let Some(result) = rescaled.checked_add(add.value as i128) else {
return None;
};
if result > i64::MAX as i128 || result < i64::MIN as i128 {
None
} else {
Some(Self {
value: result as i64,
})
}
}
#[inline(always)]
pub const fn try_mul(self, rhs: Self) -> Result<Self, OverflowError> {
match self.checked_mul(rhs) {
Some(v) => Ok(v),
None => Err(OverflowError),
}
}
#[inline(always)]
pub const fn try_div(self, rhs: Self) -> Result<Self, DivError> {
if rhs.value == 0 {
return Err(DivError::DivisionByZero);
}
match self.checked_div(rhs) {
Some(v) => Ok(v),
None => Err(DivError::Overflow),
}
}
}
use crate::wide;
impl<const D: u8> Decimal<i128, D> {
const FAST_MUL_THRESHOLD: u128 = 1u128 << 64;
#[inline(always)]
pub fn checked_mul(self, rhs: Self) -> Option<Self> {
if self.value == 0 || rhs.value == 0 {
return Some(Self::ZERO);
}
let result_negative = (self.value < 0) != (rhs.value < 0);
let a = self.value.unsigned_abs();
let b = rhs.value.unsigned_abs();
if a < Self::FAST_MUL_THRESHOLD && b < Self::FAST_MUL_THRESHOLD {
let product = a * b;
let quotient = product / (Self::SCALE as u128);
return Self::from_unsigned(quotient, result_negative);
}
let (prod_low, prod_high) = wide::mul_wide(a, b);
let quotient = wide::div_192_by_const(prod_low, prod_high, Self::SCALE as u128)?;
Self::from_unsigned(quotient, result_negative)
}
#[inline(always)]
pub fn checked_div(self, rhs: Self) -> Option<Self> {
if rhs.value == 0 {
return None;
}
if self.value == 0 {
return Some(Self::ZERO);
}
let result_negative = (self.value < 0) != (rhs.value < 0);
let a = self.value.unsigned_abs();
let b = rhs.value.unsigned_abs();
let scale = Self::SCALE as u128;
let (prod_low, prod_high) = wide::mul_u128_by_small(a, scale);
let quotient = wide::div_192_by_u128(prod_low, prod_high, b)?;
Self::from_unsigned(quotient, result_negative)
}
#[inline(always)]
pub fn saturating_mul(self, rhs: Self) -> Self {
self.checked_mul(rhs).unwrap_or({
if (self.value > 0) == (rhs.value > 0) {
Self::MAX
} else {
Self::MIN
}
})
}
#[inline(always)]
pub fn wrapping_mul(self, rhs: Self) -> Self {
if self.value == 0 || rhs.value == 0 {
return Self::ZERO;
}
let result_negative = (self.value < 0) != (rhs.value < 0);
let a = self.value.unsigned_abs();
let b = rhs.value.unsigned_abs();
let (prod_low, prod_high) = wide::mul_wide(a, b);
let quotient = wide::div_192_by_const_wrapping(prod_low, prod_high, Self::SCALE as u128);
if result_negative {
Self {
value: (quotient as i128).wrapping_neg(),
}
} else {
Self {
value: quotient as i128,
}
}
}
#[inline(always)]
pub fn saturating_div(self, rhs: Self) -> Self {
assert!(rhs.value != 0, "division by zero");
self.checked_div(rhs).unwrap_or({
if (self.value > 0) == (rhs.value > 0) {
Self::MAX
} else {
Self::MIN
}
})
}
#[inline(always)]
pub fn wrapping_div(self, rhs: Self) -> Self {
assert!(rhs.value != 0, "division by zero");
let result_negative = (self.value < 0) != (rhs.value < 0);
let a = self.value.unsigned_abs();
let b = rhs.value.unsigned_abs();
let scale = Self::SCALE as u128;
let (prod_low, prod_high) = wide::mul_u128_by_small(a, scale);
let quotient = wide::div_192_by_u128_wrapping(prod_low, prod_high, b);
if result_negative {
Self {
value: (quotient as i128).wrapping_neg(),
}
} else {
Self {
value: quotient as i128,
}
}
}
#[inline(always)]
pub const fn mul_int(self, rhs: i128) -> Option<Self> {
match self.value.checked_mul(rhs) {
Some(v) => Some(Self { value: v }),
None => None,
}
}
#[inline(always)]
pub fn mul_add(self, mul: Self, add: Self) -> Option<Self> {
if self.value == 0 || mul.value == 0 {
return Some(add);
}
let product = self.checked_mul(mul)?;
product.checked_add(add)
}
#[inline(always)]
pub fn try_mul(self, rhs: Self) -> Result<Self, OverflowError> {
self.checked_mul(rhs).ok_or(OverflowError)
}
#[inline(always)]
pub fn try_div(self, rhs: Self) -> Result<Self, DivError> {
if rhs.value == 0 {
return Err(DivError::DivisionByZero);
}
self.checked_div(rhs).ok_or(DivError::Overflow)
}
#[inline(always)]
fn from_unsigned(quotient: u128, negative: bool) -> Option<Self> {
if negative {
if quotient > (i128::MAX as u128) + 1 {
return None;
}
Some(Self {
value: (quotient as i128).wrapping_neg(),
})
} else {
if quotient > i128::MAX as u128 {
return None;
}
Some(Self {
value: quotient as i128,
})
}
}
}