use crate::backing::Backing;
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct Decimal<B: Backing, const DECIMALS: u8> {
pub(crate) value: B,
}
macro_rules! impl_decimal_core {
($backing:ty, $pow10_fn:path, $max_exp:expr) => {
impl<const D: u8> Decimal<$backing, D> {
pub const SCALE: $backing = {
assert!(
(D as u32) <= $max_exp,
"DECIMALS too large for backing type"
);
$pow10_fn(D)
};
pub const DECIMALS: u8 = D;
#[inline(always)]
pub const fn from_raw(value: $backing) -> Self {
Self { value }
}
#[inline(always)]
pub const fn to_raw(self) -> $backing {
self.value
}
pub const fn new(integer: $backing, fractional: $backing) -> Self {
let Some(scaled) = integer.checked_mul(Self::SCALE) else {
panic!("overflow in Decimal::new: integer part too large")
};
let value = if integer >= 0 {
let Some(v) = scaled.checked_add(fractional) else {
panic!("overflow in Decimal::new")
};
v
} else {
let Some(v) = scaled.checked_sub(fractional) else {
panic!("overflow in Decimal::new")
};
v
};
Self { value }
}
pub const fn from_parts(
integer: $backing,
fractional: $backing,
negative: bool,
) -> Option<Self> {
let Some(scaled) = integer.checked_mul(Self::SCALE) else {
return None;
};
let Some(abs) = scaled.checked_add(fractional) else {
return None;
};
if negative {
match abs.checked_neg() {
Some(v) => Some(Self { value: v }),
None => None,
}
} else {
Some(Self { value: abs })
}
}
#[inline(always)]
pub const fn is_zero(self) -> bool {
self.value == 0
}
#[inline(always)]
pub const fn is_positive(self) -> bool {
self.value > 0
}
#[inline(always)]
pub const fn is_negative(self) -> bool {
self.value < 0
}
#[inline(always)]
pub const fn signum(self) -> $backing {
self.value.signum()
}
}
impl<const D: u8> Default for Decimal<$backing, D> {
#[inline]
fn default() -> Self {
Self::ZERO
}
}
};
}
use crate::pow10::{pow10_i32, pow10_i64, pow10_i128};
impl_decimal_core!(i32, pow10_i32, 9);
impl_decimal_core!(i64, pow10_i64, 18);
impl_decimal_core!(i128, pow10_i128, 38);