macro_rules! decl_from_primitive {
(wide $Type:ident, $Storage:ty, $Src:ty) => {
impl<const SCALE: u32> ::core::convert::From<$Src> for $Type<SCALE> {
#[inline]
fn from(value: $Src) -> Self {
let widened: $Storage = $crate::wide_int::wide_cast(value as i128);
Self(widened * Self::multiplier())
}
}
};
($Type:ident, $Storage:ty, $Src:ty) => {
impl<const SCALE: u32> ::core::convert::From<$Src> for $Type<SCALE> {
#[inline]
fn from(value: $Src) -> Self {
Self((value as $Storage) * Self::multiplier())
}
}
};
}
pub(crate) use decl_from_primitive;
macro_rules! decl_cross_width_widening {
(wide $Dest:ident, $DestStorage:ty, $Src:ident, $SrcStorage:ty) => {
impl<const SCALE: u32> ::core::convert::From<$Src<SCALE>> for $Dest<SCALE> {
#[inline]
fn from(value: $Src<SCALE>) -> Self {
Self($crate::wide_int::wide_cast(value.to_bits()))
}
}
};
($Dest:ident, $DestStorage:ty, $Src:ident, $SrcStorage:ty) => {
impl<const SCALE: u32> ::core::convert::From<$Src<SCALE>> for $Dest<SCALE> {
#[inline]
fn from(value: $Src<SCALE>) -> Self {
Self(value.to_bits() as $DestStorage)
}
}
};
}
pub(crate) use decl_cross_width_widening;
macro_rules! decl_cross_width_narrowing {
(wide $Dest:ident, $DestStorage:ty, $Src:ident, $SrcStorage:ty) => {
impl<const SCALE: u32> ::core::convert::TryFrom<$Src<SCALE>> for $Dest<SCALE> {
type Error = $crate::error::ConvertError;
#[inline]
fn try_from(value: $Src<SCALE>) -> ::core::result::Result<Self, Self::Error> {
let bits = value.to_bits();
let dest_max: $SrcStorage = $crate::wide_int::wide_cast(<$DestStorage>::MAX);
let dest_min: $SrcStorage = $crate::wide_int::wide_cast(<$DestStorage>::MIN);
if bits > dest_max || bits < dest_min {
return ::core::result::Result::Err(
$crate::error::ConvertError::Overflow,
);
}
::core::result::Result::Ok(Self($crate::wide_int::wide_cast(bits)))
}
}
};
($Dest:ident, $DestStorage:ty, $Src:ident, $SrcStorage:ty) => {
impl<const SCALE: u32> ::core::convert::TryFrom<$Src<SCALE>> for $Dest<SCALE> {
type Error = $crate::error::ConvertError;
#[inline]
fn try_from(value: $Src<SCALE>) -> ::core::result::Result<Self, Self::Error> {
let bits = value.to_bits();
if bits > (<$DestStorage>::MAX as $SrcStorage)
|| bits < (<$DestStorage>::MIN as $SrcStorage)
{
return ::core::result::Result::Err(
$crate::error::ConvertError::Overflow,
);
}
::core::result::Result::Ok(Self(bits as $DestStorage))
}
}
};
}
pub(crate) use decl_cross_width_narrowing;
macro_rules! decl_try_from_i128 {
(wide $Type:ident, $Storage:ty) => {
impl<const SCALE: u32> ::core::convert::TryFrom<i128> for $Type<SCALE> {
type Error = $crate::error::ConvertError;
#[inline]
fn try_from(value: i128) -> ::core::result::Result<Self, Self::Error> {
let widened: $Storage = $crate::wide_int::wide_cast(value);
let scaled = widened
.checked_mul(Self::multiplier())
.ok_or($crate::error::ConvertError::Overflow)?;
::core::result::Result::Ok(Self(scaled))
}
}
};
($Type:ident, $Storage:ty) => {
impl<const SCALE: u32> ::core::convert::TryFrom<i128> for $Type<SCALE> {
type Error = $crate::error::ConvertError;
#[inline]
fn try_from(value: i128) -> ::core::result::Result<Self, Self::Error> {
let m: i128 = Self::multiplier() as i128;
let scaled = value
.checked_mul(m)
.ok_or($crate::error::ConvertError::Overflow)?;
if scaled > <$Storage>::MAX as i128 || scaled < <$Storage>::MIN as i128 {
return ::core::result::Result::Err(
$crate::error::ConvertError::Overflow,
);
}
::core::result::Result::Ok(Self(scaled as $Storage))
}
}
};
}
pub(crate) use decl_try_from_i128;
macro_rules! decl_try_from_u128 {
(wide $Type:ident, $Storage:ty) => {
impl<const SCALE: u32> ::core::convert::TryFrom<u128> for $Type<SCALE> {
type Error = $crate::error::ConvertError;
#[inline]
fn try_from(value: u128) -> ::core::result::Result<Self, Self::Error> {
let widened: $Storage = <$Storage>::from_u128(value);
let scaled = widened
.checked_mul(Self::multiplier())
.ok_or($crate::error::ConvertError::Overflow)?;
::core::result::Result::Ok(Self(scaled))
}
}
};
($Type:ident, $Storage:ty) => {
impl<const SCALE: u32> ::core::convert::TryFrom<u128> for $Type<SCALE> {
type Error = $crate::error::ConvertError;
#[inline]
fn try_from(value: u128) -> ::core::result::Result<Self, Self::Error> {
let as_i128: i128 = i128::try_from(value)
.map_err(|_| $crate::error::ConvertError::Overflow)?;
<Self as ::core::convert::TryFrom<i128>>::try_from(as_i128)
}
}
};
}
pub(crate) use decl_try_from_u128;
macro_rules! decl_try_from_f64 {
(wide $Type:ident, $Storage:ty) => {
impl<const SCALE: u32> ::core::convert::TryFrom<f64> for $Type<SCALE> {
type Error = $crate::error::ConvertError;
#[inline]
fn try_from(value: f64) -> ::core::result::Result<Self, Self::Error> {
if !value.is_finite() {
return ::core::result::Result::Err(
$crate::error::ConvertError::NotFinite,
);
}
let mult_f64: f64 = Self::multiplier().as_f64();
let scaled = value * mult_f64;
let storage_max_f64: f64 = <$Storage>::MAX.as_f64();
let storage_min_f64: f64 = <$Storage>::MIN.as_f64();
if !(storage_min_f64..storage_max_f64).contains(&scaled) {
return ::core::result::Result::Err(
$crate::error::ConvertError::Overflow,
);
}
::core::result::Result::Ok(Self(<$Storage>::from_f64(scaled)))
}
}
};
($Type:ident, $Storage:ty) => {
impl<const SCALE: u32> ::core::convert::TryFrom<f64> for $Type<SCALE> {
type Error = $crate::error::ConvertError;
#[inline]
fn try_from(value: f64) -> ::core::result::Result<Self, Self::Error> {
if !value.is_finite() {
return ::core::result::Result::Err(
$crate::error::ConvertError::NotFinite,
);
}
let scaled = value * (Self::multiplier() as f64);
let storage_max_f64 = <$Storage>::MAX as f64;
let storage_min_f64 = <$Storage>::MIN as f64;
if !(storage_min_f64..storage_max_f64).contains(&scaled) {
return ::core::result::Result::Err(
$crate::error::ConvertError::Overflow,
);
}
::core::result::Result::Ok(Self(scaled as $Storage))
}
}
};
}
macro_rules! decl_try_from_f32 {
(wide $Type:ident, $Storage:ty) => {
$crate::macros::conversions::decl_try_from_f32!($Type, $Storage);
};
($Type:ident, $Storage:ty) => {
impl<const SCALE: u32> ::core::convert::TryFrom<f32> for $Type<SCALE> {
type Error = $crate::error::ConvertError;
#[inline]
fn try_from(value: f32) -> ::core::result::Result<Self, Self::Error> {
<Self as ::core::convert::TryFrom<f64>>::try_from(value as f64)
}
}
};
}
pub(crate) use decl_try_from_f32;
pub(crate) use decl_try_from_f64;
macro_rules! decl_decimal_int_conversion_methods {
(wide $Type:ident, $Storage:ty, $IntSrc:ty) => {
impl<const SCALE: u32> $Type<SCALE> {
#[inline]
pub fn from_int(value: $IntSrc) -> Self {
let widened: $Storage = $crate::wide_int::wide_cast(value as i128);
Self(widened * Self::multiplier())
}
#[inline]
pub fn from_i32(value: i32) -> Self {
let widened: $Storage = $crate::wide_int::wide_cast(value as i128);
Self(widened * Self::multiplier())
}
#[inline]
pub fn to_int(self) -> i64 {
self.to_int_with($crate::rounding::DEFAULT_ROUNDING_MODE)
}
#[inline]
pub fn to_int_with(
self,
mode: $crate::rounding::RoundingMode,
) -> i64 {
let zero = <$Storage>::from_str_radix("0", 10)
.expect("wide decimal: invalid base-10 literal");
let one = <$Storage>::from_str_radix("1", 10)
.expect("wide decimal: invalid base-10 literal");
let raw = self.0;
let divisor = Self::multiplier();
let quotient = raw / divisor;
let remainder = raw % divisor;
let int_rounded: $Storage = if remainder == zero {
quotient
} else {
let abs_rem = remainder.unsigned_abs();
let half = divisor.unsigned_abs() >> 1;
let non_negative = !raw.is_negative();
match mode {
$crate::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::rounding::RoundingMode::HalfAwayFromZero => {
if abs_rem < half {
quotient
} else if non_negative {
quotient + one
} else {
quotient - one
}
}
$crate::rounding::RoundingMode::HalfTowardZero => {
if abs_rem > half {
if non_negative { quotient + one } else { quotient - one }
} else {
quotient
}
}
$crate::rounding::RoundingMode::Trunc => quotient,
$crate::rounding::RoundingMode::Floor => {
if non_negative { quotient } else { quotient - one }
}
$crate::rounding::RoundingMode::Ceiling => {
if non_negative { quotient + one } else { quotient }
}
}
};
let i64_max: $Storage = $crate::wide_int::wide_cast(i64::MAX);
let i64_min: $Storage = $crate::wide_int::wide_cast(i64::MIN);
if int_rounded > i64_max {
i64::MAX
} else if int_rounded < i64_min {
i64::MIN
} else {
$crate::wide_int::wide_cast::<_, i64>(int_rounded)
}
}
}
};
($Type:ident, $Storage:ty, $IntSrc:ty) => {
impl<const SCALE: u32> $Type<SCALE> {
#[inline]
pub fn from_int(value: $IntSrc) -> Self {
Self((value as $Storage) * Self::multiplier())
}
#[inline]
pub fn from_i32(value: i32) -> Self {
Self((value as $Storage) * Self::multiplier())
}
#[inline]
pub fn to_int(self) -> i64 {
self.to_int_with($crate::rounding::DEFAULT_ROUNDING_MODE)
}
#[inline]
pub fn to_int_with(
self,
mode: $crate::rounding::RoundingMode,
) -> i64 {
let raw = self.0 as i128;
let divisor = Self::multiplier() as i128;
let quotient = raw / divisor;
let remainder = raw % divisor;
let int_rounded: i128 = if remainder == 0 {
quotient
} else {
let abs_rem = remainder.unsigned_abs();
let half = (divisor / 2) as u128;
match mode {
$crate::rounding::RoundingMode::HalfToEven => {
if abs_rem < half {
quotient
} else if abs_rem > half {
if raw >= 0 { quotient + 1 } else { quotient - 1 }
} else if quotient % 2 == 0 {
quotient
} else if raw >= 0 {
quotient + 1
} else {
quotient - 1
}
}
$crate::rounding::RoundingMode::HalfAwayFromZero => {
if abs_rem < half {
quotient
} else if raw >= 0 {
quotient + 1
} else {
quotient - 1
}
}
$crate::rounding::RoundingMode::HalfTowardZero => {
if abs_rem > half {
if raw >= 0 { quotient + 1 } else { quotient - 1 }
} else {
quotient
}
}
$crate::rounding::RoundingMode::Trunc => quotient,
$crate::rounding::RoundingMode::Floor => {
if raw >= 0 { quotient } else { quotient - 1 }
}
$crate::rounding::RoundingMode::Ceiling => {
if raw >= 0 { quotient + 1 } else { quotient }
}
}
};
if int_rounded > i64::MAX as i128 {
i64::MAX
} else if int_rounded < i64::MIN as i128 {
i64::MIN
} else {
int_rounded as i64
}
}
}
};
}
pub(crate) use decl_decimal_int_conversion_methods;