#[cfg(feature = "radix_engine_fuzzing")]
use arbitrary::Arbitrary;
use core::cmp::Ordering;
use num_bigint::BigInt;
use num_traits::{Pow, Zero};
use sbor::rust::convert::TryFrom;
use sbor::rust::fmt;
use sbor::rust::format;
use sbor::rust::ops::*;
use sbor::rust::prelude::*;
use sbor::*;
#[cfg(feature = "radix_engine_fuzzing")]
use serde::{Deserialize, Serialize};
use crate::data::manifest::ManifestCustomValueKind;
use crate::data::scrypto::*;
use crate::math::bnum_integer::*;
use crate::math::rounding_mode::*;
use crate::math::traits::*;
use crate::math::PreciseDecimal;
use crate::well_known_scrypto_custom_type;
use crate::*;
use super::CheckedTruncate;
#[cfg_attr(
feature = "radix_engine_fuzzing",
derive(Arbitrary, Serialize, Deserialize)
)]
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Decimal(pub I192);
impl Default for Decimal {
fn default() -> Self {
Self::zero()
}
}
macro_rules! fmt_remainder {
() => {
"{:018}"
};
}
impl Decimal {
pub const MIN: Self = Self(I192::MIN);
pub const MAX: Self = Self(I192::MAX);
pub const BITS: usize = I192::BITS as usize;
pub const SCALE: u32 = 18;
pub const ZERO: Self = Self(I192::ZERO);
pub const ONE_HUNDREDTH: Self = Self(I192::from_digits([10_u64.pow(Decimal::SCALE - 2), 0, 0]));
pub const ONE_TENTH: Self = Self(I192::from_digits([10_u64.pow(Decimal::SCALE - 1), 0, 0]));
pub const ONE: Self = Self(I192::from_digits([10_u64.pow(Decimal::SCALE), 0, 0]));
pub const TEN: Self = Self(I192::from_digits([10_u64.pow(Decimal::SCALE + 1), 0, 0]));
pub const ONE_HUNDRED: Self = Self(I192::from_digits([7766279631452241920, 0x5, 0]));
pub const fn zero() -> Self {
Self::ZERO
}
pub const fn one() -> Self {
Self::ONE
}
pub fn is_zero(&self) -> bool {
self.0 == I192::ZERO
}
pub fn is_positive(&self) -> bool {
self.0 > I192::ZERO
}
pub fn is_negative(&self) -> bool {
self.0 < I192::ZERO
}
pub fn checked_abs(&self) -> Option<Self> {
if *self != Self::MIN {
Some(Self(self.0.abs()))
} else {
None
}
}
pub fn checked_floor(&self) -> Option<Self> {
self.checked_round(0, RoundingMode::ToNegativeInfinity)
}
pub fn checked_ceiling(&self) -> Option<Self> {
self.checked_round(0, RoundingMode::ToPositiveInfinity)
}
pub fn checked_round<T: Into<i32>>(
&self,
decimal_places: T,
mode: RoundingMode,
) -> Option<Self> {
let decimal_places = decimal_places.into();
assert!(decimal_places <= Self::SCALE as i32);
assert!(decimal_places >= 0);
let n = Self::SCALE - decimal_places as u32;
let divisor: I192 = I192::TEN.pow(n);
let positive_remainder = {
let remainder = self.0 % divisor;
match remainder.cmp(&I192::ZERO) {
Ordering::Less => divisor + remainder,
Ordering::Equal => return Some(*self),
Ordering::Greater => remainder,
}
};
let resolved_strategy =
ResolvedRoundingStrategy::from_mode(mode, self.is_positive(), || {
let midpoint = divisor >> 1; positive_remainder.cmp(&midpoint)
});
let rounded_subunits = match resolved_strategy {
ResolvedRoundingStrategy::RoundUp => {
let to_add = divisor
.checked_sub(positive_remainder)
.expect("Always safe");
self.0.checked_add(to_add)?
}
ResolvedRoundingStrategy::RoundDown => self.0.checked_sub(positive_remainder)?,
ResolvedRoundingStrategy::RoundToEven => {
let double_divisor = divisor << 1; if self.is_positive() {
let rounded_down = self.0.checked_sub(positive_remainder)?;
if rounded_down % double_divisor == I192::ZERO {
rounded_down
} else {
rounded_down.checked_add(divisor)?
}
} else {
let to_add = divisor
.checked_sub(positive_remainder)
.expect("Always safe");
let rounded_up = self.0.checked_add(to_add)?;
if rounded_up % double_divisor == I192::ZERO {
rounded_up
} else {
rounded_up.checked_sub(divisor)?
}
}
}
};
Some(Self(rounded_subunits))
}
pub fn checked_powi(&self, exp: i64) -> Option<Self> {
let one_256 = I256::from(Self::ONE.0);
let base_256 = I256::from(self.0);
let div = |x: i64, y: i64| x.checked_div(y);
let sub = |x: i64, y: i64| x.checked_sub(y);
let mul = |x: i64, y: i64| x.checked_mul(y);
if exp < 0 {
let dec_192 = I192::try_from((one_256 * one_256).checked_div(base_256)?).ok()?;
let exp = mul(exp, -1)?;
return Self(dec_192).checked_powi(exp);
}
if exp == 0 {
return Some(Self::ONE);
}
if exp == 1 {
return Some(*self);
}
if exp % 2 == 0 {
let dec_192 = I192::try_from(base_256.checked_mul(base_256)? / one_256).ok()?;
let exp = div(exp, 2)?;
Self(dec_192).checked_powi(exp)
} else {
let dec_192 = I192::try_from(base_256.checked_mul(base_256)? / one_256).ok()?;
let sub_dec = Self(dec_192);
let exp = div(sub(exp, 1)?, 2)?;
let b = sub_dec.checked_powi(exp)?;
self.checked_mul(b)
}
}
pub fn checked_sqrt(&self) -> Option<Self> {
if self.is_negative() {
return None;
}
if self.is_zero() {
return Some(Self::ZERO);
}
let self_256 = I256::from(self.0);
let correct_nb = self_256 * I256::from(Self::ONE.0);
let sqrt = I192::try_from(correct_nb.sqrt()).ok()?;
Some(Self(sqrt))
}
pub fn checked_cbrt(&self) -> Option<Self> {
if self.is_zero() {
return Some(Self::ZERO);
}
let self_320 = I320::from(self.0);
let correct_nb = self_320 * I320::from(Self::ONE.0).pow(2);
let cbrt = I192::try_from(correct_nb.cbrt()).ok()?;
Some(Self(cbrt))
}
pub fn checked_nth_root(&self, n: u32) -> Option<Self> {
if (self.is_negative() && n % 2 == 0) || n == 0 {
None
} else if n == 1 {
Some(*self)
} else {
if self.is_zero() {
return Some(Self::ZERO);
}
let self_bigint = BigInt::from(self.0);
let correct_nb = self_bigint * BigInt::from(Self::ONE.0).pow(n - 1);
let nth_root = I192::try_from(correct_nb.nth_root(n)).unwrap();
Some(Decimal(nth_root))
}
}
}
macro_rules! from_primitive_type {
($($type:ident),*) => {
$(
impl From<$type> for Decimal {
fn from(val: $type) -> Self {
Self(I192::from(val) * Self::ONE.0)
}
}
)*
};
}
macro_rules! to_primitive_type {
($($type:ident),*) => {
$(
impl TryFrom<Decimal> for $type {
type Error = ParseDecimalError;
fn try_from(val: Decimal) -> Result<Self, Self::Error> {
let rounded = val.checked_round(0, RoundingMode::ToZero).ok_or(ParseDecimalError::Overflow)?;
let fraction = val.checked_sub(rounded).ok_or(Self::Error::Overflow)?;
if !fraction.is_zero() {
Err(Self::Error::InvalidDigit)
}
else {
let i_192 = rounded.0 / I192::TEN.pow(Decimal::SCALE);
$type::try_from(i_192)
.map_err(|_| Self::Error::Overflow)
}
}
}
impl TryFrom<&Decimal> for $type {
type Error = ParseDecimalError;
fn try_from(val: &Decimal) -> Result<Self, Self::Error> {
$type::try_from(*val)
}
}
)*
}
}
from_primitive_type!(i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize);
to_primitive_type!(i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize);
impl TryFrom<&str> for Decimal {
type Error = ParseDecimalError;
fn try_from(val: &str) -> Result<Self, Self::Error> {
Self::from_str(val)
}
}
impl TryFrom<String> for Decimal {
type Error = ParseDecimalError;
fn try_from(val: String) -> Result<Self, Self::Error> {
Self::from_str(&val)
}
}
impl From<bool> for Decimal {
fn from(val: bool) -> Self {
if val {
Self::ONE
} else {
Self::ZERO
}
}
}
impl CheckedNeg<Decimal> for Decimal {
type Output = Self;
#[inline]
fn checked_neg(self) -> Option<Self::Output> {
let c = self.0.checked_neg();
c.map(Self)
}
}
impl CheckedAdd<Decimal> for Decimal {
type Output = Self;
#[inline]
fn checked_add(self, other: Self) -> Option<Self::Output> {
let a = self.0;
let b = other.0;
let c = a.checked_add(b);
c.map(Self)
}
}
impl CheckedSub<Decimal> for Decimal {
type Output = Self;
#[inline]
fn checked_sub(self, other: Self) -> Option<Self::Output> {
let a = self.0;
let b = other.0;
let c = a.checked_sub(b);
c.map(Self)
}
}
impl CheckedMul<Decimal> for Decimal {
type Output = Self;
#[inline]
fn checked_mul(self, other: Self) -> Option<Self> {
let a = I256::from(self.0);
let b = I256::from(other.0);
let mut c = a.checked_mul(b)?;
c = c.checked_div(I256::from(Self::ONE.0))?;
let c_192 = I192::try_from(c).ok();
c_192.map(Self)
}
}
impl CheckedDiv<Decimal> for Decimal {
type Output = Self;
#[inline]
fn checked_div(self, other: Self) -> Option<Self> {
let a = I256::from(self.0);
let b = I256::from(other.0);
let mut c = a.checked_mul(I256::from(Self::ONE.0))?;
c = c.checked_div(b)?;
let c_192 = I192::try_from(c).ok();
c_192.map(Self)
}
}
impl Neg for Decimal {
type Output = Self;
#[inline]
fn neg(self) -> Self::Output {
self.checked_neg().expect("Overflow")
}
}
impl Add<Decimal> for Decimal {
type Output = Self;
#[inline]
fn add(self, other: Self) -> Self::Output {
self.checked_add(other).expect("Overflow")
}
}
impl Sub<Decimal> for Decimal {
type Output = Self;
#[inline]
fn sub(self, other: Self) -> Self::Output {
self.checked_sub(other).expect("Overflow")
}
}
impl Mul<Decimal> for Decimal {
type Output = Self;
#[inline]
fn mul(self, other: Self) -> Self::Output {
self.checked_mul(other).expect("Overflow")
}
}
impl Div<Decimal> for Decimal {
type Output = Self;
#[inline]
fn div(self, other: Self) -> Self::Output {
self.checked_div(other)
.expect("Overflow or division by zero")
}
}
impl AddAssign<Decimal> for Decimal {
#[inline]
fn add_assign(&mut self, other: Self) {
*self = *self + other;
}
}
impl SubAssign<Decimal> for Decimal {
#[inline]
fn sub_assign(&mut self, other: Self) {
*self = *self - other;
}
}
impl MulAssign<Decimal> for Decimal {
#[inline]
fn mul_assign(&mut self, other: Self) {
*self = *self * other;
}
}
impl DivAssign<Decimal> for Decimal {
#[inline]
fn div_assign(&mut self, other: Self) {
*self = *self / other;
}
}
macro_rules! impl_arith_ops {
($type:ident) => {
impl CheckedAdd<$type> for Decimal {
type Output = Self;
fn checked_add(self, other: $type) -> Option<Self::Output> {
self.checked_add(Self::try_from(other).ok()?)
}
}
impl CheckedSub<$type> for Decimal {
type Output = Self;
fn checked_sub(self, other: $type) -> Option<Self::Output> {
self.checked_sub(Self::try_from(other).ok()?)
}
}
impl CheckedMul<$type> for Decimal {
type Output = Self;
fn checked_mul(self, other: $type) -> Option<Self::Output> {
self.checked_mul(Self::try_from(other).ok()?)
}
}
impl CheckedDiv<$type> for Decimal {
type Output = Self;
fn checked_div(self, other: $type) -> Option<Self::Output> {
self.checked_div(Self::try_from(other).ok()?)
}
}
impl Add<$type> for Decimal {
type Output = Self;
#[inline]
fn add(self, other: $type) -> Self::Output {
self.checked_add(other).expect("Overflow")
}
}
impl Sub<$type> for Decimal {
type Output = Self;
#[inline]
fn sub(self, other: $type) -> Self::Output {
self.checked_sub(other).expect("Overflow")
}
}
impl Mul<$type> for Decimal {
type Output = Self;
#[inline]
fn mul(self, other: $type) -> Self::Output {
self.checked_mul(other).expect("Overflow")
}
}
impl Div<$type> for Decimal {
type Output = Self;
#[inline]
fn div(self, other: $type) -> Self::Output {
self.checked_div(other)
.expect("Overflow or division by zero")
}
}
impl Add<Decimal> for $type {
type Output = Decimal;
#[inline]
fn add(self, other: Decimal) -> Self::Output {
other + self
}
}
impl Sub<Decimal> for $type {
type Output = Decimal;
#[inline]
fn sub(self, other: Decimal) -> Self::Output {
Decimal::try_from(self)
.expect("Overflow")
.checked_sub(other)
.expect("Overflow")
}
}
impl Mul<Decimal> for $type {
type Output = Decimal;
#[inline]
fn mul(self, other: Decimal) -> Self::Output {
other * self
}
}
impl Div<Decimal> for $type {
type Output = Decimal;
#[inline]
fn div(self, other: Decimal) -> Self::Output {
Decimal::try_from(self)
.expect("Overflow")
.checked_div(other)
.expect("Overflow or division by zero")
}
}
impl AddAssign<$type> for Decimal {
#[inline]
fn add_assign(&mut self, other: $type) {
*self = *self + other;
}
}
impl SubAssign<$type> for Decimal {
#[inline]
fn sub_assign(&mut self, other: $type) {
*self = *self - other;
}
}
impl MulAssign<$type> for Decimal {
#[inline]
fn mul_assign(&mut self, other: $type) {
*self = *self * other;
}
}
impl DivAssign<$type> for Decimal {
#[inline]
fn div_assign(&mut self, other: $type) {
*self = *self / other;
}
}
};
}
impl_arith_ops!(u8);
impl_arith_ops!(u16);
impl_arith_ops!(u32);
impl_arith_ops!(u64);
impl_arith_ops!(u128);
impl_arith_ops!(usize);
impl_arith_ops!(i8);
impl_arith_ops!(i16);
impl_arith_ops!(i32);
impl_arith_ops!(i64);
impl_arith_ops!(i128);
impl_arith_ops!(isize);
impl_arith_ops!(I192);
impl_arith_ops!(I256);
impl_arith_ops!(I320);
impl_arith_ops!(I448);
impl_arith_ops!(I512);
impl_arith_ops!(U192);
impl_arith_ops!(U256);
impl_arith_ops!(U320);
impl_arith_ops!(U448);
impl_arith_ops!(U512);
macro_rules! impl_arith_ops_non_primitives {
($type:ident) => {
impl CheckedAdd<Decimal> for $type {
type Output = Decimal;
#[inline]
fn checked_add(self, other: Decimal) -> Option<Self::Output> {
other.checked_add(self)
}
}
impl CheckedSub<Decimal> for $type {
type Output = Decimal;
fn checked_sub(self, other: Decimal) -> Option<Self::Output> {
Decimal::try_from(self).ok()?.checked_sub(other)
}
}
impl CheckedMul<Decimal> for $type {
type Output = Decimal;
#[inline]
fn checked_mul(self, other: Decimal) -> Option<Self::Output> {
other.checked_mul(self)
}
}
impl CheckedDiv<Decimal> for $type {
type Output = Decimal;
fn checked_div(self, other: Decimal) -> Option<Self::Output> {
Decimal::try_from(self).ok()?.checked_div(other)
}
}
};
}
impl_arith_ops_non_primitives!(I192);
impl_arith_ops_non_primitives!(I256);
impl_arith_ops_non_primitives!(I320);
impl_arith_ops_non_primitives!(I448);
impl_arith_ops_non_primitives!(I512);
impl_arith_ops_non_primitives!(U192);
impl_arith_ops_non_primitives!(U256);
impl_arith_ops_non_primitives!(U320);
impl_arith_ops_non_primitives!(U448);
impl_arith_ops_non_primitives!(U512);
impl TryFrom<&[u8]> for Decimal {
type Error = ParseDecimalError;
fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
if slice.len() == Self::BITS / 8 {
let val = I192::try_from(slice).expect("Length should have already been checked.");
Ok(Self(val))
} else {
Err(ParseDecimalError::InvalidLength(slice.len()))
}
}
}
impl Decimal {
pub fn to_vec(&self) -> Vec<u8> {
self.0.to_le_bytes().to_vec()
}
}
well_known_scrypto_custom_type!(
Decimal,
ScryptoCustomValueKind::Decimal,
Type::Decimal,
Decimal::BITS / 8,
DECIMAL_TYPE,
decimal_type_data
);
manifest_type!(Decimal, ManifestCustomValueKind::Decimal, Decimal::BITS / 8);
impl FromStr for Decimal {
type Err = ParseDecimalError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let v: Vec<&str> = s.split('.').collect();
if v.len() > 2 {
return Err(ParseDecimalError::MoreThanOneDecimalPoint);
}
let integer_part = match I192::from_str(v[0]) {
Ok(val) => val,
Err(err) => match err {
ParseI192Error::NegativeToUnsigned => {
unreachable!("NegativeToUnsigned is only for parsing unsigned types, not I192")
}
ParseI192Error::Overflow => return Err(ParseDecimalError::Overflow),
ParseI192Error::InvalidLength => {
unreachable!("InvalidLength is only for parsing &[u8], not &str")
}
ParseI192Error::InvalidDigit => return Err(ParseDecimalError::InvalidDigit),
ParseI192Error::Empty => return Err(ParseDecimalError::EmptyIntegralPart),
},
};
let mut subunits = integer_part
.checked_mul(Self::ONE.0)
.ok_or(ParseDecimalError::Overflow)?;
if v.len() == 2 {
let scale = if let Some(scale) = Self::SCALE.checked_sub(v[1].len() as u32) {
Ok(scale)
} else {
Err(Self::Err::MoreThanEighteenDecimalPlaces)
}?;
let fractional_part = match I192::from_str(v[1]) {
Ok(val) => val,
Err(err) => match err {
ParseI192Error::NegativeToUnsigned => {
unreachable!(
"NegativeToUnsigned is only for parsing unsigned types, no I192"
)
}
ParseI192Error::Overflow => return Err(ParseDecimalError::Overflow),
ParseI192Error::InvalidLength => {
unreachable!("InvalidLength is only for parsing &[u8], not &str")
}
ParseI192Error::InvalidDigit => return Err(ParseDecimalError::InvalidDigit),
ParseI192Error::Empty => return Err(ParseDecimalError::EmptyFractionalPart),
},
};
let fractional_subunits = fractional_part
.checked_mul(I192::TEN.pow(scale))
.expect("No overflow possible");
if integer_part.is_negative() || v[0].starts_with('-') {
subunits = subunits
.checked_sub(fractional_subunits)
.ok_or(ParseDecimalError::Overflow)?;
} else {
subunits = subunits
.checked_add(fractional_subunits)
.ok_or(ParseDecimalError::Overflow)?;
}
}
Ok(Self(subunits))
}
}
impl fmt::Display for Decimal {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
const MULTIPLIER: I192 = Decimal::ONE.0;
let quotient = self.0 / MULTIPLIER;
let remainder = self.0 % MULTIPLIER;
if !remainder.is_zero() {
let mut sign = "".to_string();
if remainder < I192::ZERO && quotient == I192::ZERO {
sign.push('-');
}
let rem_str = format!(fmt_remainder!(), remainder.abs());
write!(f, "{}{}.{}", sign, quotient, &rem_str.trim_end_matches('0'))
} else {
write!(f, "{}", quotient)
}
}
}
impl fmt::Debug for Decimal {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ParseDecimalError {
InvalidDigit,
Overflow,
EmptyIntegralPart,
EmptyFractionalPart,
MoreThanEighteenDecimalPlaces,
MoreThanOneDecimalPoint,
InvalidLength(usize),
}
#[cfg(not(feature = "alloc"))]
impl std::error::Error for ParseDecimalError {}
#[cfg(not(feature = "alloc"))]
impl fmt::Display for ParseDecimalError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
}
}
impl TryFrom<PreciseDecimal> for Decimal {
type Error = ParseDecimalError;
fn try_from(val: PreciseDecimal) -> Result<Self, Self::Error> {
val.checked_truncate(RoundingMode::ToZero)
.ok_or(ParseDecimalError::Overflow)
}
}
macro_rules! try_from_integer {
($($t:ident),*) => {
$(
impl TryFrom<$t> for Decimal {
type Error = ParseDecimalError;
fn try_from(val: $t) -> Result<Self, Self::Error> {
match I192::try_from(val) {
Ok(val) => {
match val.checked_mul(Self::ONE.0) {
Some(mul) => Ok(Self(mul)),
None => Err(ParseDecimalError::Overflow),
}
},
Err(_) => Err(ParseDecimalError::Overflow),
}
}
}
)*
};
}
try_from_integer!(I192, I256, I320, I448, I512, U192, U256, U320, U448, U512);
#[cfg(test)]
mod tests {
use super::*;
use paste::paste;
macro_rules! test_dec {
($x:literal) => {
$crate::math::Decimal::try_from($x).unwrap()
};
}
#[test]
fn test_format_decimal() {
assert_eq!(Decimal(1i128.into()).to_string(), "0.000000000000000001");
assert_eq!(
Decimal(123456789123456789i128.into()).to_string(),
"0.123456789123456789"
);
assert_eq!(Decimal(1000000000000000000i128.into()).to_string(), "1");
assert_eq!(Decimal(123000000000000000000i128.into()).to_string(), "123");
assert_eq!(
Decimal(123456789123456789000000000000000000i128.into()).to_string(),
"123456789123456789"
);
assert_eq!(
Decimal::MAX.to_string(),
"3138550867693340381917894711603833208051.177722232017256447"
);
assert!(Decimal::MIN.is_negative());
assert_eq!(
Decimal::MIN.to_string(),
"-3138550867693340381917894711603833208051.177722232017256448"
);
}
#[test]
fn test_parse_decimal() {
assert_eq!(
Decimal::from_str("0.000000000000000001").unwrap(),
Decimal(1i128.into()),
);
assert_eq!(
Decimal::from_str("0.0000000000000000001"),
Err(ParseDecimalError::MoreThanEighteenDecimalPlaces),
);
assert_eq!(
Decimal::from_str("0.123456789123456789").unwrap(),
Decimal(123456789123456789i128.into()),
);
assert_eq!(
Decimal::from_str("1").unwrap(),
Decimal(1000000000000000000i128.into()),
);
assert_eq!(
Decimal::from_str("123456789123456789").unwrap(),
Decimal(123456789123456789000000000000000000i128.into()),
);
assert_eq!(
Decimal::from_str("3138550867693340381917894711603833208051.177722232017256447")
.unwrap(),
Decimal::MAX,
);
assert_eq!(
Decimal::from_str("3138550867693340381917894711603833208051.177722232017256448"),
Err(ParseDecimalError::Overflow),
);
assert_eq!(
Decimal::from_str("3138550867693340381917894711603833208052.177722232017256447"),
Err(ParseDecimalError::Overflow),
);
assert_eq!(
Decimal::from_str("-3138550867693340381917894711603833208051.177722232017256448")
.unwrap(),
Decimal::MIN,
);
assert_eq!(
Decimal::from_str("-3138550867693340381917894711603833208051.177722232017256449"),
Err(ParseDecimalError::Overflow),
);
assert_eq!(
Decimal::from_str(".000000000000000231"),
Err(ParseDecimalError::EmptyIntegralPart),
);
assert_eq!(
Decimal::from_str("231."),
Err(ParseDecimalError::EmptyFractionalPart),
);
assert_eq!(test_dec!("0"), Decimal::ZERO);
assert_eq!(test_dec!("1"), Decimal::ONE);
assert_eq!(test_dec!("0.1"), Decimal::ONE_TENTH);
assert_eq!(test_dec!("10"), Decimal::TEN);
assert_eq!(test_dec!("100"), Decimal::ONE_HUNDRED);
assert_eq!(test_dec!("0.01"), Decimal::ONE_HUNDREDTH);
assert_eq!("0", Decimal::ZERO.to_string());
assert_eq!("1", Decimal::ONE.to_string());
assert_eq!("0.1", Decimal::ONE_TENTH.to_string());
assert_eq!("10", Decimal::TEN.to_string());
assert_eq!("100", Decimal::ONE_HUNDRED.to_string());
assert_eq!("0.01", Decimal::ONE_HUNDREDTH.to_string());
}
#[test]
fn test_add_decimal() {
let a = Decimal::from(5u32);
let b = Decimal::from(7u32);
assert_eq!(a.checked_add(b).unwrap().to_string(), "12");
}
#[test]
fn test_add_overflow_decimal() {
assert!(Decimal::MAX.checked_add(Decimal::ONE).is_none());
}
#[test]
fn test_sub_decimal() {
let a = Decimal::from(5u32);
let b = Decimal::from(7u32);
assert_eq!(a.checked_sub(b).unwrap().to_string(), "-2");
assert_eq!(b.checked_sub(a).unwrap().to_string(), "2");
}
#[test]
fn test_sub_overflow_decimal() {
assert!(Decimal::MIN.checked_sub(Decimal::ONE).is_none());
}
#[test]
fn test_mul_decimal() {
let a = Decimal::from(5u32);
let b = Decimal::from(7u32);
assert_eq!(a.checked_mul(b).unwrap().to_string(), "35");
let a = Decimal::from_str("1000000000").unwrap();
let b = Decimal::from_str("1000000000").unwrap();
assert_eq!(a.checked_mul(b).unwrap().to_string(), "1000000000000000000");
let a = Decimal::MAX;
let b = test_dec!(1);
assert_eq!(a.checked_mul(b).unwrap(), Decimal::MAX);
}
#[test]
fn test_mul_to_max_decimal() {
let a = Decimal::MAX.checked_sqrt().unwrap();
a.checked_mul(a).unwrap();
}
#[test]
fn test_mul_to_minimum_overflow_decimal() {
let a = Decimal::MAX.checked_sqrt().unwrap();
assert!(a.checked_mul(a + Decimal(I192::ONE)).is_none());
}
#[test]
fn test_mul_overflow_by_small_decimal() {
assert!(Decimal::MAX
.checked_mul(test_dec!("1.000000000000000001"))
.is_none());
}
#[test]
fn test_mul_overflow_by_a_lot_decimal() {
assert!(Decimal::MAX.checked_mul(test_dec!("1.1")).is_none());
}
#[test]
fn test_mul_neg_overflow_decimal() {
assert!(Decimal::MAX
.checked_neg()
.unwrap()
.checked_mul(test_dec!("-1.000000000000000001"))
.is_none());
}
#[test]
fn test_div_by_zero_decimal() {
let a = Decimal::from(5u32);
let b = Decimal::from(0u32);
assert!(a.checked_div(b).is_none());
}
#[test]
fn test_powi_exp_overflow_decimal() {
let a = Decimal::from(5u32);
let b = i64::MIN;
assert!(a.checked_powi(b).is_none());
}
#[test]
fn test_1_powi_max_decimal() {
let a = Decimal::from(1u32);
let b = i64::MAX;
assert_eq!(a.checked_powi(b).unwrap().to_string(), "1");
}
#[test]
fn test_1_powi_min_decimal() {
let a = Decimal::from(1u32);
let b = i64::MAX - 1;
assert_eq!(a.checked_powi(b).unwrap().to_string(), "1");
}
#[test]
fn test_powi_max_decimal() {
let _max = Decimal::MAX.checked_powi(1);
let _max_sqrt = Decimal::MAX.checked_sqrt().unwrap();
let _max_cbrt = Decimal::MAX.checked_cbrt().unwrap();
let _max_dec_2 = _max_sqrt.checked_powi(2).unwrap();
let _max_dec_3 = _max_cbrt.checked_powi(3).unwrap();
}
#[test]
fn test_div_decimal() {
let a = Decimal::from(5u32);
let b = Decimal::from(7u32);
assert_eq!(
a.checked_div(b).unwrap().to_string(),
"0.714285714285714285"
);
assert_eq!(b.checked_div(a).unwrap().to_string(), "1.4");
assert_eq!(
Decimal::MAX.checked_div(test_dec!(1)).unwrap(),
Decimal::MAX
);
}
#[test]
fn test_div_negative_decimal() {
let a = Decimal::from(-42);
let b = Decimal::from(2);
assert_eq!(a.checked_div(b).unwrap().to_string(), "-21");
}
#[test]
fn test_0_pow_0_decimal() {
let a = test_dec!("0");
assert_eq!((a.checked_powi(0).unwrap()).to_string(), "1");
}
#[test]
fn test_0_powi_1_decimal() {
let a = test_dec!("0");
assert_eq!((a.checked_powi(1).unwrap()).to_string(), "0");
}
#[test]
fn test_0_powi_10_decimal() {
let a = test_dec!("0");
assert_eq!((a.checked_powi(10).unwrap()).to_string(), "0");
}
#[test]
fn test_1_powi_0_decimal() {
let a = test_dec!(1);
assert_eq!((a.checked_powi(0).unwrap()).to_string(), "1");
}
#[test]
fn test_1_powi_1_decimal() {
let a = test_dec!(1);
assert_eq!((a.checked_powi(1).unwrap()).to_string(), "1");
}
#[test]
fn test_1_powi_10_decimal() {
let a = test_dec!(1);
assert_eq!((a.checked_powi(10).unwrap()).to_string(), "1");
}
#[test]
fn test_2_powi_0_decimal() {
let a = test_dec!("2");
assert_eq!(a.checked_powi(0).unwrap().to_string(), "1");
}
#[test]
fn test_2_powi_3724_decimal() {
let a = test_dec!("1.000234891009084238");
assert_eq!(
a.checked_powi(3724).unwrap().to_string(),
"2.397991232254669619"
);
}
#[test]
fn test_2_powi_2_decimal() {
let a = test_dec!("2");
assert_eq!(a.checked_powi(2).unwrap().to_string(), "4");
}
#[test]
fn test_2_powi_3_decimal() {
let a = test_dec!("2");
assert_eq!(a.checked_powi(3).unwrap().to_string(), "8");
}
#[test]
fn test_10_powi_3_decimal() {
let a = test_dec!("10");
assert_eq!(a.checked_powi(3).unwrap().to_string(), "1000");
}
#[test]
fn test_5_powi_2_decimal() {
let a = test_dec!("5");
assert_eq!(a.checked_powi(2).unwrap().to_string(), "25");
}
#[test]
fn test_5_powi_minus2_decimal() {
let a = test_dec!("5");
assert_eq!(a.checked_powi(-2).unwrap().to_string(), "0.04");
}
#[test]
fn test_10_powi_minus3_decimal() {
let a = test_dec!("10");
assert_eq!(a.checked_powi(-3).unwrap().to_string(), "0.001");
}
#[test]
fn test_minus10_powi_minus3_decimal() {
let a = test_dec!("-10");
assert_eq!(a.checked_powi(-3).unwrap().to_string(), "-0.001");
}
#[test]
fn test_minus10_powi_minus2_decimal() {
let a = test_dec!("-10");
assert_eq!(a.checked_powi(-2).unwrap().to_string(), "0.01");
}
#[test]
fn test_minus05_powi_minus2_decimal() {
let a = test_dec!("-0.5");
assert_eq!(a.checked_powi(-2).unwrap().to_string(), "4");
}
#[test]
fn test_minus05_powi_minus3_decimal() {
let a = test_dec!("-0.5");
assert_eq!(a.checked_powi(-3).unwrap().to_string(), "-8");
}
#[test]
fn test_10_powi_15_decimal() {
let a = test_dec!(10i128);
assert_eq!(a.checked_powi(15).unwrap().to_string(), "1000000000000000");
}
#[test]
fn test_10_powi_16_decimal() {
let a = Decimal(10i128.into());
assert_eq!(a.checked_powi(16).unwrap().to_string(), "0");
}
#[test]
fn test_one_and_zero_decimal() {
assert_eq!(Decimal::one().to_string(), "1");
assert_eq!(Decimal::zero().to_string(), "0");
}
#[test]
fn test_dec_string_decimal_decimal() {
assert_eq!(
test_dec!("1.123456789012345678").to_string(),
"1.123456789012345678"
);
assert_eq!(test_dec!("-5.6").to_string(), "-5.6");
}
#[test]
fn test_dec_string_decimal() {
assert_eq!(test_dec!(1).to_string(), "1");
assert_eq!(test_dec!("0").to_string(), "0");
}
#[test]
fn test_dec_int_decimal() {
assert_eq!(test_dec!(1).to_string(), "1");
assert_eq!(test_dec!(5).to_string(), "5");
}
#[test]
fn test_dec_bool_decimal() {
assert_eq!((test_dec!(false)).to_string(), "0");
}
#[test]
fn test_floor_decimal() {
assert_eq!(
Decimal::MAX.checked_floor().unwrap(),
test_dec!("3138550867693340381917894711603833208051")
);
assert_eq!(test_dec!("1.2").checked_floor().unwrap(), test_dec!("1"));
assert_eq!(test_dec!("1.0").checked_floor().unwrap(), test_dec!("1"));
assert_eq!(test_dec!("0.9").checked_floor().unwrap(), test_dec!("0"));
assert_eq!(test_dec!("0").checked_floor().unwrap(), test_dec!("0"));
assert_eq!(test_dec!("-0.1").checked_floor().unwrap(), test_dec!("-1"));
assert_eq!(test_dec!("-1").checked_floor().unwrap(), test_dec!("-1"));
assert_eq!(test_dec!("-5.2").checked_floor().unwrap(), test_dec!("-6"));
assert_eq!(
test_dec!("-3138550867693340381917894711603833208050.177722232017256448") .checked_floor()
.unwrap(),
test_dec!("-3138550867693340381917894711603833208051")
);
assert_eq!(
test_dec!("-3138550867693340381917894711603833208050.000000000000000001")
.checked_floor()
.unwrap(),
test_dec!("-3138550867693340381917894711603833208051")
);
assert_eq!(
test_dec!("-3138550867693340381917894711603833208051.000000000000000000")
.checked_floor()
.unwrap(),
test_dec!("-3138550867693340381917894711603833208051")
);
assert!(Decimal::MIN.checked_floor().is_none());
assert!(
test_dec!("-3138550867693340381917894711603833208051.000000000000000001")
.checked_floor()
.is_none()
);
}
#[test]
fn test_abs_decimal() {
assert_eq!(test_dec!(-2).checked_abs().unwrap(), test_dec!(2));
assert_eq!(test_dec!(2).checked_abs().unwrap(), test_dec!(2));
assert_eq!(test_dec!(0).checked_abs().unwrap(), test_dec!(0));
assert_eq!(Decimal::MAX.checked_abs().unwrap(), Decimal::MAX);
assert!(Decimal::MIN.checked_abs().is_none());
}
#[test]
fn test_ceiling_decimal() {
assert_eq!(test_dec!("1.2").checked_ceiling().unwrap(), test_dec!("2"));
assert_eq!(test_dec!("1.0").checked_ceiling().unwrap(), test_dec!("1"));
assert_eq!(test_dec!("0.9").checked_ceiling().unwrap(), test_dec!("1"));
assert_eq!(test_dec!("0").checked_ceiling().unwrap(), test_dec!("0"));
assert_eq!(test_dec!("-0.1").checked_ceiling().unwrap(), test_dec!("0"));
assert_eq!(test_dec!("-1").checked_ceiling().unwrap(), test_dec!("-1"));
assert_eq!(
test_dec!("-5.2").checked_ceiling().unwrap(),
test_dec!("-5")
);
assert_eq!(
Decimal::MIN.checked_ceiling().unwrap(),
test_dec!("-3138550867693340381917894711603833208051")
);
assert_eq!(
test_dec!("3138550867693340381917894711603833208050.177722232017256447") .checked_ceiling()
.unwrap(),
test_dec!("3138550867693340381917894711603833208051")
);
assert_eq!(
test_dec!("3138550867693340381917894711603833208050.000000000000000000")
.checked_ceiling()
.unwrap(),
test_dec!("3138550867693340381917894711603833208050")
);
assert!(Decimal::MAX.checked_ceiling().is_none());
assert!(
test_dec!("3138550867693340381917894711603833208051.000000000000000001")
.checked_ceiling()
.is_none()
);
}
#[test]
fn test_rounding_to_zero_decimal() {
let mode = RoundingMode::ToZero;
assert_eq!(
test_dec!("1.2").checked_round(0, mode).unwrap(),
test_dec!("1")
);
assert_eq!(
test_dec!("1.0").checked_round(0, mode).unwrap(),
test_dec!("1")
);
assert_eq!(
test_dec!("0.9").checked_round(0, mode).unwrap(),
test_dec!("0")
);
assert_eq!(
test_dec!("0").checked_round(0, mode).unwrap(),
test_dec!("0")
);
assert_eq!(
test_dec!("-0.1").checked_round(0, mode).unwrap(),
test_dec!("0")
);
assert_eq!(
test_dec!("-1").checked_round(0, mode).unwrap(),
test_dec!("-1")
);
assert_eq!(
test_dec!("-5.2").checked_round(0, mode).unwrap(),
test_dec!("-5")
);
assert_eq!(
Decimal::MAX.checked_round(0, mode).unwrap(),
test_dec!("3138550867693340381917894711603833208051")
);
assert_eq!(
Decimal::MIN.checked_round(0, mode).unwrap(),
test_dec!("-3138550867693340381917894711603833208051")
);
}
#[test]
fn test_rounding_away_from_zero_decimal() {
let mode = RoundingMode::AwayFromZero;
assert_eq!(
test_dec!("1.2").checked_round(0, mode).unwrap(),
test_dec!("2")
);
assert_eq!(
test_dec!("1.0").checked_round(0, mode).unwrap(),
test_dec!("1")
);
assert_eq!(
test_dec!("0.9").checked_round(0, mode).unwrap(),
test_dec!("1")
);
assert_eq!(
test_dec!("0").checked_round(0, mode).unwrap(),
test_dec!("0")
);
assert_eq!(
test_dec!("-0.1").checked_round(0, mode).unwrap(),
test_dec!("-1")
);
assert_eq!(
test_dec!("-1").checked_round(0, mode).unwrap(),
test_dec!("-1")
);
assert_eq!(
test_dec!("-5.2").checked_round(0, mode).unwrap(),
test_dec!("-6")
);
assert_eq!(
test_dec!("-3138550867693340381917894711603833208050.9")
.checked_round(0, mode)
.unwrap(),
test_dec!("-3138550867693340381917894711603833208051")
);
assert_eq!(
test_dec!("3138550867693340381917894711603833208050.9")
.checked_round(0, mode)
.unwrap(),
test_dec!("3138550867693340381917894711603833208051")
);
assert!(Decimal::MIN.checked_round(0, mode).is_none());
assert!(test_dec!("-3138550867693340381917894711603833208051.1")
.checked_round(0, mode)
.is_none());
assert!(Decimal::MAX.checked_round(0, mode).is_none());
assert!(test_dec!("3138550867693340381917894711603833208051.1")
.checked_round(0, mode)
.is_none());
}
#[test]
fn test_rounding_midpoint_toward_zero_decimal() {
let mode = RoundingMode::ToNearestMidpointTowardZero;
assert_eq!(
test_dec!("5.5").checked_round(0, mode).unwrap(),
test_dec!("5")
);
assert_eq!(
test_dec!("2.5").checked_round(0, mode).unwrap(),
test_dec!("2")
);
assert_eq!(
test_dec!("1.6").checked_round(0, mode).unwrap(),
test_dec!("2")
);
assert_eq!(
test_dec!("1.1").checked_round(0, mode).unwrap(),
test_dec!("1")
);
assert_eq!(
test_dec!("1.0").checked_round(0, mode).unwrap(),
test_dec!("1")
);
assert_eq!(
test_dec!("-1.0").checked_round(0, mode).unwrap(),
test_dec!("-1")
);
assert_eq!(
test_dec!("-1.1").checked_round(0, mode).unwrap(),
test_dec!("-1")
);
assert_eq!(
test_dec!("-1.6").checked_round(0, mode).unwrap(),
test_dec!("-2")
);
assert_eq!(
test_dec!("-2.5").checked_round(0, mode).unwrap(),
test_dec!("-2")
);
assert_eq!(
test_dec!("-5.5").checked_round(0, mode).unwrap(),
test_dec!("-5")
);
assert_eq!(
Decimal::MAX.checked_round(0, mode).unwrap(),
test_dec!("3138550867693340381917894711603833208051")
);
assert_eq!(
Decimal::MIN.checked_round(0, mode).unwrap(),
test_dec!("-3138550867693340381917894711603833208051")
);
}
#[test]
fn test_rounding_midpoint_away_from_zero_decimal() {
let mode = RoundingMode::ToNearestMidpointAwayFromZero;
assert_eq!(
test_dec!("5.5").checked_round(0, mode).unwrap(),
test_dec!("6")
);
assert_eq!(
test_dec!("2.5").checked_round(0, mode).unwrap(),
test_dec!("3")
);
assert_eq!(
test_dec!("1.6").checked_round(0, mode).unwrap(),
test_dec!("2")
);
assert_eq!(
test_dec!("1.1").checked_round(0, mode).unwrap(),
test_dec!("1")
);
assert_eq!(
test_dec!("1.0").checked_round(0, mode).unwrap(),
test_dec!("1")
);
assert_eq!(
test_dec!("-1.0").checked_round(0, mode).unwrap(),
test_dec!("-1")
);
assert_eq!(
test_dec!("-1.1").checked_round(0, mode).unwrap(),
test_dec!("-1")
);
assert_eq!(
test_dec!("-1.6").checked_round(0, mode).unwrap(),
test_dec!("-2")
);
assert_eq!(
test_dec!("-2.5").checked_round(0, mode).unwrap(),
test_dec!("-3")
);
assert_eq!(
test_dec!("-5.5").checked_round(0, mode).unwrap(),
test_dec!("-6")
);
assert_eq!(
Decimal::MAX.checked_round(0, mode).unwrap(),
test_dec!("3138550867693340381917894711603833208051")
);
assert_eq!(
Decimal::MIN.checked_round(0, mode).unwrap(),
test_dec!("-3138550867693340381917894711603833208051")
);
}
#[test]
fn test_rounding_midpoint_nearest_even_zero_decimal() {
let mode = RoundingMode::ToNearestMidpointToEven;
assert_eq!(
test_dec!("5.5").checked_round(0, mode).unwrap(),
test_dec!("6")
);
assert_eq!(
test_dec!("2.5").checked_round(0, mode).unwrap(),
test_dec!("2")
);
assert_eq!(
test_dec!("1.6").checked_round(0, mode).unwrap(),
test_dec!("2")
);
assert_eq!(
test_dec!("1.1").checked_round(0, mode).unwrap(),
test_dec!("1")
);
assert_eq!(
test_dec!("1.0").checked_round(0, mode).unwrap(),
test_dec!("1")
);
assert_eq!(
test_dec!("-1.0").checked_round(0, mode).unwrap(),
test_dec!("-1")
);
assert_eq!(
test_dec!("-1.1").checked_round(0, mode).unwrap(),
test_dec!("-1")
);
assert_eq!(
test_dec!("-1.6").checked_round(0, mode).unwrap(),
test_dec!("-2")
);
assert_eq!(
test_dec!("-2.5").checked_round(0, mode).unwrap(),
test_dec!("-2")
);
assert_eq!(
test_dec!("-5.5").checked_round(0, mode).unwrap(),
test_dec!("-6")
);
assert_eq!(
Decimal::MAX.checked_round(0, mode).unwrap(),
test_dec!("3138550867693340381917894711603833208051")
);
assert_eq!(
Decimal::MIN.checked_round(0, mode).unwrap(),
test_dec!("-3138550867693340381917894711603833208051")
);
}
#[test]
fn test_various_decimal_places_decimal() {
let num = test_dec!("2.4595");
let mode = RoundingMode::AwayFromZero;
assert_eq!(num.checked_round(0, mode).unwrap(), test_dec!("3"));
assert_eq!(num.checked_round(1, mode).unwrap(), test_dec!("2.5"));
assert_eq!(num.checked_round(2, mode).unwrap(), test_dec!("2.46"));
assert_eq!(num.checked_round(3, mode).unwrap(), test_dec!("2.46"));
assert_eq!(
test_dec!("3138550867693340381917894711603833208050.177722232017256447")
.checked_round(1, mode)
.unwrap(),
test_dec!("3138550867693340381917894711603833208050.2")
);
assert_eq!(
test_dec!("-3138550867693340381917894711603833208050.177722232017256448")
.checked_round(1, mode)
.unwrap(),
test_dec!("-3138550867693340381917894711603833208050.2")
);
let mode = RoundingMode::ToZero;
assert_eq!(num.checked_round(0, mode).unwrap(), test_dec!("2"));
assert_eq!(num.checked_round(1, mode).unwrap(), test_dec!("2.4"));
assert_eq!(num.checked_round(2, mode).unwrap(), test_dec!("2.45"));
assert_eq!(num.checked_round(3, mode).unwrap(), test_dec!("2.459"));
let mode = RoundingMode::ToPositiveInfinity;
assert_eq!(num.checked_round(0, mode).unwrap(), test_dec!("3"));
assert_eq!(num.checked_round(1, mode).unwrap(), test_dec!("2.5"));
assert_eq!(num.checked_round(2, mode).unwrap(), test_dec!("2.46"));
assert_eq!(num.checked_round(3, mode).unwrap(), test_dec!("2.46"));
let mode = RoundingMode::ToNegativeInfinity;
assert_eq!(num.checked_round(0, mode).unwrap(), test_dec!("2"));
assert_eq!(num.checked_round(1, mode).unwrap(), test_dec!("2.4"));
assert_eq!(num.checked_round(2, mode).unwrap(), test_dec!("2.45"));
assert_eq!(num.checked_round(3, mode).unwrap(), test_dec!("2.459"));
let mode = RoundingMode::ToNearestMidpointAwayFromZero;
assert_eq!(num.checked_round(0, mode).unwrap(), test_dec!("2"));
assert_eq!(num.checked_round(1, mode).unwrap(), test_dec!("2.5"));
assert_eq!(num.checked_round(2, mode).unwrap(), test_dec!("2.46"));
assert_eq!(num.checked_round(3, mode).unwrap(), test_dec!("2.46"));
let mode = RoundingMode::ToNearestMidpointTowardZero;
assert_eq!(num.checked_round(0, mode).unwrap(), test_dec!("2"));
assert_eq!(num.checked_round(1, mode).unwrap(), test_dec!("2.5"));
assert_eq!(num.checked_round(2, mode).unwrap(), test_dec!("2.46"));
assert_eq!(num.checked_round(3, mode).unwrap(), test_dec!("2.459"));
let mode = RoundingMode::ToNearestMidpointToEven;
assert_eq!(num.checked_round(0, mode).unwrap(), test_dec!("2"));
assert_eq!(num.checked_round(1, mode).unwrap(), test_dec!("2.5"));
assert_eq!(num.checked_round(2, mode).unwrap(), test_dec!("2.46"));
assert_eq!(num.checked_round(3, mode).unwrap(), test_dec!("2.46"));
let num = test_dec!("-2.4595");
let mode = RoundingMode::AwayFromZero;
assert_eq!(num.checked_round(0, mode).unwrap(), test_dec!("-3"));
assert_eq!(num.checked_round(1, mode).unwrap(), test_dec!("-2.5"));
assert_eq!(num.checked_round(2, mode).unwrap(), test_dec!("-2.46"));
assert_eq!(num.checked_round(3, mode).unwrap(), test_dec!("-2.46"));
let mode = RoundingMode::ToZero;
assert_eq!(num.checked_round(0, mode).unwrap(), test_dec!("-2"));
assert_eq!(num.checked_round(1, mode).unwrap(), test_dec!("-2.4"));
assert_eq!(num.checked_round(2, mode).unwrap(), test_dec!("-2.45"));
assert_eq!(num.checked_round(3, mode).unwrap(), test_dec!("-2.459"));
let mode = RoundingMode::ToPositiveInfinity;
assert_eq!(num.checked_round(0, mode).unwrap(), test_dec!("-2"));
assert_eq!(num.checked_round(1, mode).unwrap(), test_dec!("-2.4"));
assert_eq!(num.checked_round(2, mode).unwrap(), test_dec!("-2.45"));
assert_eq!(num.checked_round(3, mode).unwrap(), test_dec!("-2.459"));
let mode = RoundingMode::ToNegativeInfinity;
assert_eq!(num.checked_round(0, mode).unwrap(), test_dec!("-3"));
assert_eq!(num.checked_round(1, mode).unwrap(), test_dec!("-2.5"));
assert_eq!(num.checked_round(2, mode).unwrap(), test_dec!("-2.46"));
assert_eq!(num.checked_round(3, mode).unwrap(), test_dec!("-2.46"));
let mode = RoundingMode::ToNearestMidpointAwayFromZero;
assert_eq!(num.checked_round(0, mode).unwrap(), test_dec!("-2"));
assert_eq!(num.checked_round(1, mode).unwrap(), test_dec!("-2.5"));
assert_eq!(num.checked_round(2, mode).unwrap(), test_dec!("-2.46"));
assert_eq!(num.checked_round(3, mode).unwrap(), test_dec!("-2.46"));
let mode = RoundingMode::ToNearestMidpointTowardZero;
assert_eq!(num.checked_round(0, mode).unwrap(), test_dec!("-2"));
assert_eq!(num.checked_round(1, mode).unwrap(), test_dec!("-2.5"));
assert_eq!(num.checked_round(2, mode).unwrap(), test_dec!("-2.46"));
assert_eq!(num.checked_round(3, mode).unwrap(), test_dec!("-2.459"));
let mode = RoundingMode::ToNearestMidpointToEven;
assert_eq!(num.checked_round(0, mode).unwrap(), test_dec!("-2"));
assert_eq!(num.checked_round(1, mode).unwrap(), test_dec!("-2.5"));
assert_eq!(num.checked_round(2, mode).unwrap(), test_dec!("-2.46"));
assert_eq!(num.checked_round(3, mode).unwrap(), test_dec!("-2.46"));
}
#[test]
fn test_encode_decimal_value_decimal() {
let dec = test_dec!("0");
let bytes = scrypto_encode(&dec).unwrap();
assert_eq!(bytes, {
let mut a = [0; 26];
a[0] = SCRYPTO_SBOR_V1_PAYLOAD_PREFIX;
a[1] = ScryptoValueKind::Custom(ScryptoCustomValueKind::Decimal).as_u8();
a
});
}
#[test]
fn test_decode_decimal_value_decimal() {
let dec = test_dec!("1.23456789");
let bytes = scrypto_encode(&dec).unwrap();
let decoded: Decimal = scrypto_decode(&bytes).unwrap();
assert_eq!(decoded, test_dec!("1.23456789"));
}
#[test]
fn test_from_str_decimal() {
let dec = Decimal::from_str("5.0").unwrap();
assert_eq!(dec.to_string(), "5");
}
#[test]
fn test_from_str_failure_decimal() {
let dec = Decimal::from_str("non_decimal_value");
assert_eq!(dec, Err(ParseDecimalError::InvalidDigit));
}
macro_rules! test_from_into_precise_decimal_decimal {
($(($from:expr, $expected:expr, $suffix:expr)),*) => {
paste!{
$(
#[test]
fn [<test_from_into_precise_decimal_decimal_ $suffix>]() {
let pdec = PreciseDecimal::try_from($from).unwrap();
let dec = Decimal::try_from(pdec).unwrap();
assert_eq!(dec.to_string(), $expected);
let dec: Decimal = pdec.try_into().unwrap();
assert_eq!(dec.to_string(), $expected);
}
)*
}
};
}
test_from_into_precise_decimal_decimal! {
("12345678.123456789012345678901234567890123456", "12345678.123456789012345678", 1),
("12345678.123456789012345678101234567890123456", "12345678.123456789012345678", 2),
("-12345678.123456789012345678901234567890123456", "-12345678.123456789012345678", 3),
("-12345678.123456789012345678101234567890123456", "-12345678.123456789012345678", 4),
("0.000000000000000000000000008901234567", "0", 5),
("-0.000000000000000000000000008901234567", "0", 6),
("5", "5", 7),
("12345678.1", "12345678.1", 8)
}
macro_rules! test_from_precise_decimal_decimal_overflow {
($(($from:expr, $suffix:expr)),*) => {
paste!{
$(
#[test]
fn [<test_from_precise_decimal_decimal_overflow_ $suffix>]() {
let err = Decimal::try_from($from).unwrap_err();
assert_eq!(err, ParseDecimalError::Overflow);
}
)*
}
};
}
test_from_precise_decimal_decimal_overflow! {
(PreciseDecimal::MAX, 1),
(PreciseDecimal::MIN, 2),
(PreciseDecimal::from(Decimal::MAX).checked_add(Decimal::ONE).unwrap(), 3),
(PreciseDecimal::from(Decimal::MIN).checked_sub(Decimal::ONE).unwrap(), 4)
}
macro_rules! test_try_from_integer_overflow {
($(($from:expr, $suffix:expr)),*) => {
paste!{
$(
#[test]
fn [<test_try_from_integer_overflow_ $suffix>]() {
let err = Decimal::try_from($from).unwrap_err();
assert_eq!(err, ParseDecimalError::Overflow)
}
)*
}
};
}
test_try_from_integer_overflow! {
(I192::MAX, 1),
(I192::MIN, 2),
(I192::MAX/(I192::from(10).pow(Decimal::SCALE)) + I192::ONE, 3),
(I192::MIN/(I192::from(10).pow(Decimal::SCALE)) - I192::ONE, 4),
(I256::MAX, 5),
(I256::MIN, 6),
(I320::MAX, 7),
(I320::MIN, 8),
(I448::MAX, 9),
(I448::MIN, 10),
(I512::MAX, 11),
(I512::MIN, 12),
(U192::MAX, 13),
(U256::MAX, 14),
(U320::MAX, 15),
(U448::MAX, 16),
(U512::MAX, 17)
}
macro_rules! test_try_from_integer {
($(($from:expr, $expected:expr, $suffix:expr)),*) => {
paste!{
$(
#[test]
fn [<test_try_from_integer_ $suffix>]() {
let dec = Decimal::try_from($from).unwrap();
assert_eq!(dec.to_string(), $expected)
}
)*
}
};
}
test_try_from_integer! {
(I192::ONE, "1", 1),
(-I192::ONE, "-1", 2),
(I256::ONE, "1", 3),
(-I256::ONE, "-1", 4),
(I320::ONE, "1", 5),
(-I320::ONE, "-1", 6),
(I448::ONE, "1", 7),
(-I448::ONE, "-1", 8),
(I512::ONE, "1", 9),
(-I512::ONE, "-1", 10),
(I192::MAX/(I192::from(10_u64.pow(Decimal::SCALE))), "3138550867693340381917894711603833208051", 11),
(I192::MIN/(I192::from(10_u64.pow(Decimal::SCALE))), "-3138550867693340381917894711603833208051", 12),
(U192::MIN, "0", 13),
(U256::MIN, "0", 14),
(U320::MIN, "0", 15),
(U448::MIN, "0", 16),
(U512::MIN, "0", 17)
}
#[test]
fn test_sqrt() {
let sqrt_of_42 = test_dec!(42).checked_sqrt();
let sqrt_of_0 = test_dec!(0).checked_sqrt();
let sqrt_of_negative = test_dec!("-1").checked_sqrt();
let sqrt_max = Decimal::MAX.checked_sqrt();
assert_eq!(sqrt_of_42.unwrap(), test_dec!("6.48074069840786023"));
assert_eq!(sqrt_of_0.unwrap(), test_dec!(0));
assert_eq!(sqrt_of_negative, None);
assert_eq!(
sqrt_max.unwrap(),
test_dec!("56022770974786139918.731938227458171762")
);
}
#[test]
fn test_cbrt() {
let cbrt_of_42 = test_dec!(42).checked_cbrt().unwrap();
let cbrt_of_0 = test_dec!(0).checked_cbrt().unwrap();
let cbrt_of_negative_42 = test_dec!("-42").checked_cbrt().unwrap();
let cbrt_max = Decimal::MAX.checked_cbrt().unwrap();
assert_eq!(cbrt_of_42, test_dec!("3.476026644886449786"));
assert_eq!(cbrt_of_0, test_dec!("0"));
assert_eq!(cbrt_of_negative_42, test_dec!("-3.476026644886449786"));
assert_eq!(cbrt_max, test_dec!("14641190473997.345813510937532903"));
}
#[test]
fn test_nth_root() {
let root_4_42 = test_dec!(42).checked_nth_root(4);
let root_5_42 = test_dec!(42).checked_nth_root(5);
let root_42_42 = test_dec!(42).checked_nth_root(42);
let root_neg_4_42 = test_dec!("-42").checked_nth_root(4);
let root_neg_5_42 = test_dec!("-42").checked_nth_root(5);
let root_0 = test_dec!(42).checked_nth_root(0);
assert_eq!(root_4_42.unwrap(), test_dec!("2.545729895021830518"));
assert_eq!(root_5_42.unwrap(), test_dec!("2.111785764966753912"));
assert_eq!(root_42_42.unwrap(), test_dec!("1.093072057934823618"));
assert_eq!(root_neg_4_42, None);
assert_eq!(root_neg_5_42.unwrap(), test_dec!("-2.111785764966753912"));
assert_eq!(root_0, None);
}
#[test]
fn no_panic_with_18_decimal_places() {
let string = "1.111111111111111111";
let decimal = Decimal::from_str(string);
assert!(decimal.is_ok())
}
#[test]
fn no_panic_with_19_decimal_places() {
let string = "1.1111111111111111111";
let decimal = Decimal::from_str(string);
assert!(matches!(
decimal,
Err(ParseDecimalError::MoreThanEighteenDecimalPlaces)
))
}
#[test]
fn test_neg_decimal() {
let d = Decimal::ONE;
assert_eq!(-d, test_dec!("-1"));
let d = Decimal::MAX;
assert_eq!(-d, Decimal(I192::MIN + I192::ONE));
}
#[test]
#[should_panic(expected = "Overflow")]
fn test_neg_decimal_panic() {
let d = Decimal::MIN;
let _ = -d;
}
macro_rules! test_arith_decimal_primitive {
($type:ident) => {
paste! {
#[test]
fn [<test_arith_decimal_$type>]() {
let d1 = test_dec!("2");
let u1 = $type::try_from(4).unwrap();
assert_eq!(d1.checked_add(u1).unwrap(), test_dec!("6"));
assert_eq!(d1.checked_sub(u1).unwrap(), test_dec!("-2"));
assert_eq!(d1.checked_mul(u1).unwrap(), test_dec!("8"));
assert_eq!(d1.checked_div(u1).unwrap(), test_dec!("0.5"));
let d1 = test_dec!("2");
let u1 = $type::MAX;
let d2 = Decimal::from($type::MAX);
assert_eq!(d1.checked_add(u1).unwrap(), d1.checked_add(d2).unwrap());
assert_eq!(d1.checked_sub(u1).unwrap(), d1.checked_sub(d2).unwrap());
assert_eq!(d1.checked_mul(u1).unwrap(), d1.checked_mul(d2).unwrap());
assert_eq!(d1.checked_div(u1).unwrap(), d1.checked_div(d2).unwrap());
let d1 = Decimal::from($type::MIN);
let u1 = 2 as $type;
let d2 = test_dec!("2");
assert_eq!(d1.checked_add(u1).unwrap(), d1.checked_add(d2).unwrap());
assert_eq!(d1.checked_sub(u1).unwrap(), d1.checked_sub(d2).unwrap());
assert_eq!(d1.checked_mul(u1).unwrap(), d1.checked_mul(d2).unwrap());
assert_eq!(d1.checked_div(u1).unwrap(), d1.checked_div(d2).unwrap());
}
}
};
}
test_arith_decimal_primitive!(u8);
test_arith_decimal_primitive!(u16);
test_arith_decimal_primitive!(u32);
test_arith_decimal_primitive!(u64);
test_arith_decimal_primitive!(u128);
test_arith_decimal_primitive!(usize);
test_arith_decimal_primitive!(i8);
test_arith_decimal_primitive!(i16);
test_arith_decimal_primitive!(i32);
test_arith_decimal_primitive!(i64);
test_arith_decimal_primitive!(i128);
test_arith_decimal_primitive!(isize);
macro_rules! test_arith_decimal_integer {
($type:ident) => {
paste! {
#[test]
fn [<test_arith_decimal_$type:lower>]() {
let d1 = test_dec!("2");
let u1 = $type::try_from(4).unwrap();
let u2 = $type::try_from(2).unwrap();
let d2 = test_dec!("4");
assert_eq!(d1.checked_add(u1).unwrap(), u2.checked_add(d2).unwrap());
assert_eq!(d1.checked_sub(u1).unwrap(), u2.checked_sub(d2).unwrap());
assert_eq!(d1.checked_mul(u1).unwrap(), u2.checked_mul(d2).unwrap());
assert_eq!(d1.checked_div(u1).unwrap(), u2.checked_div(d2).unwrap());
let d1 = test_dec!("2");
let u1 = $type::MAX;
assert!(d1.checked_add(u1).is_none());
assert!(d1.checked_sub(u1).is_none());
assert!(d1.checked_mul(u1).is_none());
assert!(d1.checked_div(u1).is_none());
let d1 = Decimal::MAX;
let u1 = $type::try_from(2).unwrap();
assert_eq!(d1.checked_add(u1), None);
assert_eq!(d1.checked_sub(u1).unwrap(), Decimal::MAX - test_dec!("2"));
assert_eq!(d1.checked_mul(u1), None);
assert_eq!(d1.checked_div(u1).unwrap(), Decimal::MAX / test_dec!("2"));
}
}
};
}
test_arith_decimal_integer!(I192);
test_arith_decimal_integer!(I256);
test_arith_decimal_integer!(I512);
test_arith_decimal_integer!(U192);
test_arith_decimal_integer!(U256);
test_arith_decimal_integer!(U512);
macro_rules! test_math_operands_decimal {
($type:ident) => {
paste! {
#[test]
fn [<test_math_operands_decimal_$type:lower>]() {
let d1 = test_dec!("2");
let u1 = $type::try_from(4).unwrap();
assert_eq!(d1 + u1, test_dec!("6"));
assert_eq!(d1 - u1, test_dec!("-2"));
assert_eq!(d1 * u1, test_dec!("8"));
assert_eq!(d1 / u1, test_dec!("0.5"));
let u1 = $type::try_from(2).unwrap();
let d1 = test_dec!("4");
assert_eq!(u1 + d1, test_dec!("6"));
assert_eq!(u1 - d1, test_dec!("-2"));
assert_eq!(u1 * d1, test_dec!("8"));
assert_eq!(u1 / d1, test_dec!("0.5"));
let u1 = $type::try_from(4).unwrap();
let mut d1 = test_dec!("2");
d1 += u1;
assert_eq!(d1, test_dec!("6"));
let mut d1 = test_dec!("2");
d1 -= u1;
assert_eq!(d1, test_dec!("-2"));
let mut d1 = test_dec!("2");
d1 *= u1;
assert_eq!(d1, test_dec!("8"));
let mut d1 = test_dec!("2");
d1 /= u1;
assert_eq!(d1, test_dec!("0.5"));
}
#[test]
#[should_panic(expected = "Overflow")]
fn [<test_math_add_decimal_$type:lower _panic>]() {
let d1 = Decimal::MAX;
let u1 = $type::try_from(1).unwrap();
let _ = d1 + u1;
}
#[test]
#[should_panic(expected = "Overflow")]
fn [<test_math_add_$type:lower _xdecimal_panic>]() {
let d1 = Decimal::MAX;
let u1 = $type::try_from(1).unwrap();
let _ = u1 + d1;
}
#[test]
#[should_panic(expected = "Overflow")]
fn [<test_math_sub_decimal_$type:lower _panic>]() {
let d1 = Decimal::MIN;
let u1 = $type::try_from(1).unwrap();
let _ = d1 - u1;
}
#[test]
#[should_panic(expected = "Overflow")]
fn [<test_math_sub_$type:lower _xdecimal_panic>]() {
let d1 = Decimal::MIN;
let u1 = $type::try_from(1).unwrap();
let _ = u1 - d1;
}
#[test]
#[should_panic(expected = "Overflow")]
fn [<test_math_mul_decimal_$type:lower _panic>]() {
let d1 = Decimal::MAX;
let u1 = $type::try_from(2).unwrap();
let _ = d1 * u1;
}
#[test]
#[should_panic(expected = "Overflow")]
fn [<test_math_mul_$type:lower _xdecimal_panic>]() {
let d1 = Decimal::MAX;
let u1 = $type::try_from(2).unwrap();
let _ = u1 * d1;
}
#[test]
#[should_panic(expected = "Overflow")]
fn [<test_math_div_zero_decimal_$type:lower _panic>]() {
let d1 = Decimal::MAX;
let u1 = $type::try_from(0).unwrap();
let _ = d1 / u1;
}
#[test]
#[should_panic(expected = "Overflow or division by zero")]
fn [<test_math_div_zero_$type:lower _xdecimal_panic>]() {
let d1 = Decimal::ZERO;
let u1 = $type::try_from(1).unwrap();
let _ = u1 / d1;
}
#[test]
#[should_panic(expected = "Overflow")]
fn [<test_math_add_assign_decimal_$type:lower _panic>]() {
let mut d1 = Decimal::MAX;
let u1 = $type::try_from(1).unwrap();
d1 += u1;
}
#[test]
#[should_panic(expected = "Overflow")]
fn [<test_math_sub_assign_decimal_$type:lower _panic>]() {
let mut d1 = Decimal::MIN;
let u1 = $type::try_from(1).unwrap();
d1 -= u1;
}
#[test]
#[should_panic(expected = "Overflow")]
fn [<test_math_mul_assign_decimal_$type:lower _panic>]() {
let mut d1 = Decimal::MAX;
let u1 = $type::try_from(2).unwrap();
d1 *= u1;
}
#[test]
#[should_panic(expected = "Overflow or division by zero")]
fn [<test_math_div_assign_decimal_$type:lower _panic>]() {
let mut d1 = Decimal::MAX;
let u1 = $type::try_from(0).unwrap();
d1 /= u1;
}
}
};
}
test_math_operands_decimal!(Decimal);
test_math_operands_decimal!(u8);
test_math_operands_decimal!(u16);
test_math_operands_decimal!(u32);
test_math_operands_decimal!(u64);
test_math_operands_decimal!(u128);
test_math_operands_decimal!(usize);
test_math_operands_decimal!(i8);
test_math_operands_decimal!(i16);
test_math_operands_decimal!(i32);
test_math_operands_decimal!(i64);
test_math_operands_decimal!(i128);
test_math_operands_decimal!(isize);
test_math_operands_decimal!(I192);
test_math_operands_decimal!(I256);
test_math_operands_decimal!(I320);
test_math_operands_decimal!(I448);
test_math_operands_decimal!(I512);
test_math_operands_decimal!(U192);
test_math_operands_decimal!(U256);
test_math_operands_decimal!(U320);
test_math_operands_decimal!(U448);
test_math_operands_decimal!(U512);
macro_rules! test_from_primitive_type {
($type:ident) => {
paste! {
#[test]
fn [<test_decimal_from_primitive_$type>]() {
let v = $type::try_from(1).unwrap();
assert_eq!(Decimal::from(v), test_dec!(1));
if $type::MIN != 0 {
let v = $type::try_from(-1).unwrap();
assert_eq!(Decimal::from(v), test_dec!(-1));
}
let v = $type::MAX;
assert_eq!(Decimal::from(v), Decimal::from_str(&v.to_string()).unwrap());
let v = $type::MIN;
assert_eq!(Decimal::from(v), Decimal::from_str(&v.to_string()).unwrap());
}
}
};
}
test_from_primitive_type!(u8);
test_from_primitive_type!(u16);
test_from_primitive_type!(u32);
test_from_primitive_type!(u64);
test_from_primitive_type!(u128);
test_from_primitive_type!(usize);
test_from_primitive_type!(i8);
test_from_primitive_type!(i16);
test_from_primitive_type!(i32);
test_from_primitive_type!(i64);
test_from_primitive_type!(i128);
test_from_primitive_type!(isize);
macro_rules! test_to_primitive_type {
($type:ident) => {
paste! {
#[test]
fn [<test_decimal_to_primitive_$type>]() {
let d = test_dec!(1);
let v = $type::try_from(1).unwrap();
assert_eq!($type::try_from(d).unwrap(), v);
if $type::MIN != 0 {
let d = test_dec!(-1);
let v = $type::try_from(-1).unwrap();
assert_eq!($type::try_from(d).unwrap(), v);
}
let v = $type::MAX;
let d = Decimal::from(v);
assert_eq!($type::try_from(d).unwrap(), v);
let v = $type::MIN;
let d = Decimal::from(v);
assert_eq!($type::try_from(d).unwrap(), v);
let d = Decimal::MAX;
let err = $type::try_from(d).unwrap_err();
assert_eq!(err, ParseDecimalError::InvalidDigit);
let v = $type::MAX;
let d = Decimal::from(v).checked_add(1).unwrap();
let err = $type::try_from(d).unwrap_err();
assert_eq!(err, ParseDecimalError::Overflow);
let v = $type::MIN;
let d = Decimal::from(v).checked_sub(1).unwrap();
let err = $type::try_from(d).unwrap_err();
assert_eq!(err, ParseDecimalError::Overflow);
let d = test_dec!("1.1");
let err = $type::try_from(d).unwrap_err();
assert_eq!(err, ParseDecimalError::InvalidDigit);
}
}
};
}
test_to_primitive_type!(u8);
test_to_primitive_type!(u16);
test_to_primitive_type!(u32);
test_to_primitive_type!(u64);
test_to_primitive_type!(u128);
test_to_primitive_type!(usize);
test_to_primitive_type!(i8);
test_to_primitive_type!(i16);
test_to_primitive_type!(i32);
test_to_primitive_type!(i64);
test_to_primitive_type!(i128);
test_to_primitive_type!(isize);
}