use crate::{Boolean, Double, Float, Integer, TooLargeForIntegerError};
use std::fmt;
use std::fmt::Write;
use std::str::FromStr;
const DECIMAL_PART_DIGITS: u32 = 18;
const DECIMAL_PART_POW: i128 = 1_000_000_000_000_000_000;
const DECIMAL_PART_POW_MINUS_ONE: i128 = 100_000_000_000_000_000;
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Copy, Hash, Default)]
#[repr(packed(8))]
pub struct Decimal {
value: i128, }
impl Decimal {
pub const MAX: Self = Self { value: i128::MAX };
pub const MIN: Self = Self { value: i128::MIN };
#[cfg(test)]
pub const STEP: Self = Self { value: 1 };
#[inline]
pub const fn new(i: i128, n: u32) -> Result<Self, TooLargeForDecimalError> {
let Some(shift) = DECIMAL_PART_DIGITS.checked_sub(n) else {
return Err(TooLargeForDecimalError);
};
let Some(value) = i.checked_mul(10_i128.pow(shift)) else {
return Err(TooLargeForDecimalError);
};
Ok(Self { value })
}
pub(crate) const fn new_from_i128_unchecked(value: i128) -> Self {
Self {
value: value * DECIMAL_PART_POW,
}
}
#[inline]
#[must_use]
pub fn from_be_bytes(bytes: [u8; 16]) -> Self {
Self {
value: i128::from_be_bytes(bytes),
}
}
#[inline]
#[must_use]
pub fn to_be_bytes(self) -> [u8; 16] {
self.value.to_be_bytes()
}
#[inline]
#[must_use]
pub fn checked_add(self, rhs: impl Into<Self>) -> Option<Self> {
Some(Self {
value: self.value.checked_add(rhs.into().value)?,
})
}
#[inline]
#[must_use]
pub fn checked_sub(self, rhs: impl Into<Self>) -> Option<Self> {
Some(Self {
value: self.value.checked_sub(rhs.into().value)?,
})
}
#[inline]
#[must_use]
pub fn checked_mul(self, rhs: impl Into<Self>) -> Option<Self> {
let mut left = self.value;
let mut shift_left = 0_u32;
if left != 0 {
while left % 10 == 0 {
left /= 10;
shift_left += 1;
}
}
let mut right = rhs.into().value;
let mut shift_right = 0_u32;
if right != 0 {
while right % 10 == 0 {
right /= 10;
shift_right += 1;
}
}
let shift = (shift_left + shift_right).checked_sub(DECIMAL_PART_DIGITS)?;
Some(Self {
value: left
.checked_mul(right)?
.checked_mul(10_i128.checked_pow(shift)?)?,
})
}
#[inline]
#[must_use]
pub fn checked_div(self, rhs: impl Into<Self>) -> Option<Self> {
let mut left = self.value;
let mut shift_left = 0_u32;
if left != 0 {
while let Some(r) = left.checked_mul(10) {
left = r;
shift_left += 1;
}
}
let mut right = rhs.into().value;
let mut shift_right = 0_u32;
if right != 0 {
while right % 10 == 0 {
right /= 10;
shift_right += 1;
}
}
let shift = (shift_left + shift_right).checked_sub(DECIMAL_PART_DIGITS)?;
Some(Self {
value: left
.checked_div(right)?
.checked_div(10_i128.checked_pow(shift)?)?,
})
}
#[inline]
#[must_use]
pub fn checked_rem(self, rhs: impl Into<Self>) -> Option<Self> {
Some(Self {
value: self.value.checked_rem(rhs.into().value)?,
})
}
#[inline]
#[must_use]
pub fn checked_rem_euclid(self, rhs: impl Into<Self>) -> Option<Self> {
Some(Self {
value: self.value.checked_rem_euclid(rhs.into().value)?,
})
}
#[inline]
#[must_use]
pub fn checked_neg(self) -> Option<Self> {
Some(Self {
value: self.value.checked_neg()?,
})
}
#[inline]
#[must_use]
pub fn checked_abs(self) -> Option<Self> {
Some(Self {
value: self.value.checked_abs()?,
})
}
#[inline]
#[must_use]
pub fn checked_round(self) -> Option<Self> {
let value = self.value / DECIMAL_PART_POW_MINUS_ONE;
Some(Self {
value: if value >= 0 {
value / 10 + i128::from(value % 10 >= 5)
} else {
value / 10 - i128::from(-value % 10 > 5)
}
.checked_mul(DECIMAL_PART_POW)?,
})
}
#[inline]
#[must_use]
pub fn checked_ceil(self) -> Option<Self> {
Some(Self {
value: if self.value > 0 && self.value % DECIMAL_PART_POW != 0 {
self.value / DECIMAL_PART_POW + 1
} else {
self.value / DECIMAL_PART_POW
}
.checked_mul(DECIMAL_PART_POW)?,
})
}
#[inline]
#[must_use]
pub fn checked_floor(self) -> Option<Self> {
Some(Self {
value: if self.value >= 0 || self.value % DECIMAL_PART_POW == 0 {
self.value / DECIMAL_PART_POW
} else {
self.value / DECIMAL_PART_POW - 1
}
.checked_mul(DECIMAL_PART_POW)?,
})
}
#[inline]
#[must_use]
pub const fn is_negative(self) -> bool {
self.value < 0
}
#[inline]
#[must_use]
pub const fn is_positive(self) -> bool {
self.value > 0
}
#[inline]
#[must_use]
pub fn is_identical_with(self, other: Self) -> bool {
self == other
}
#[inline]
#[must_use]
pub(super) const fn as_i128(self) -> i128 {
self.value / DECIMAL_PART_POW
}
}
impl From<bool> for Decimal {
#[inline]
fn from(value: bool) -> Self {
Self {
value: i128::from(value) * DECIMAL_PART_POW,
}
}
}
impl From<i8> for Decimal {
#[inline]
fn from(value: i8) -> Self {
Self {
value: i128::from(value) * DECIMAL_PART_POW,
}
}
}
impl From<i16> for Decimal {
#[inline]
fn from(value: i16) -> Self {
Self {
value: i128::from(value) * DECIMAL_PART_POW,
}
}
}
impl From<i32> for Decimal {
#[inline]
fn from(value: i32) -> Self {
Self {
value: i128::from(value) * DECIMAL_PART_POW,
}
}
}
impl From<i64> for Decimal {
#[inline]
fn from(value: i64) -> Self {
Self {
value: i128::from(value) * DECIMAL_PART_POW,
}
}
}
impl From<u8> for Decimal {
#[inline]
fn from(value: u8) -> Self {
Self {
value: i128::from(value) * DECIMAL_PART_POW,
}
}
}
impl From<u16> for Decimal {
#[inline]
fn from(value: u16) -> Self {
Self {
value: i128::from(value) * DECIMAL_PART_POW,
}
}
}
impl From<u32> for Decimal {
#[inline]
fn from(value: u32) -> Self {
Self {
value: i128::from(value) * DECIMAL_PART_POW,
}
}
}
impl From<u64> for Decimal {
#[inline]
fn from(value: u64) -> Self {
Self {
value: i128::from(value) * DECIMAL_PART_POW,
}
}
}
impl From<Integer> for Decimal {
#[inline]
fn from(value: Integer) -> Self {
i64::from(value).into()
}
}
impl TryFrom<i128> for Decimal {
type Error = TooLargeForDecimalError;
#[inline]
fn try_from(value: i128) -> Result<Self, Self::Error> {
Ok(Self {
value: value
.checked_mul(DECIMAL_PART_POW)
.ok_or(TooLargeForDecimalError)?,
})
}
}
impl TryFrom<u128> for Decimal {
type Error = TooLargeForDecimalError;
#[inline]
fn try_from(value: u128) -> Result<Self, Self::Error> {
Ok(Self {
value: i128::try_from(value)
.map_err(|_| TooLargeForDecimalError)?
.checked_mul(DECIMAL_PART_POW)
.ok_or(TooLargeForDecimalError)?,
})
}
}
impl From<Boolean> for Decimal {
#[inline]
fn from(value: Boolean) -> Self {
bool::from(value).into()
}
}
impl TryFrom<Float> for Decimal {
type Error = TooLargeForDecimalError;
#[inline]
fn try_from(value: Float) -> Result<Self, Self::Error> {
Double::from(value).try_into()
}
}
impl TryFrom<Double> for Decimal {
type Error = TooLargeForDecimalError;
#[inline]
#[allow(clippy::cast_precision_loss, clippy::cast_possible_truncation)]
fn try_from(value: Double) -> Result<Self, Self::Error> {
let shifted = f64::from(value) * (DECIMAL_PART_POW as f64);
if (i128::MIN as f64) <= shifted && shifted <= (i128::MAX as f64) {
Ok(Self {
value: shifted as i128,
})
} else {
Err(TooLargeForDecimalError)
}
}
}
impl From<Decimal> for Float {
#[inline]
#[allow(clippy::cast_precision_loss)]
fn from(value: Decimal) -> Self {
Double::from(value).into()
}
}
impl From<Decimal> for Double {
#[inline]
#[allow(clippy::cast_precision_loss)]
fn from(value: Decimal) -> Self {
let mut value = value.value;
let mut shift = DECIMAL_PART_POW;
if value != 0 {
while shift != 1 && value % 10 == 0 {
value /= 10;
shift /= 10;
}
}
((value as f64) / (shift as f64)).into()
}
}
impl TryFrom<Decimal> for Integer {
type Error = TooLargeForIntegerError;
#[inline]
fn try_from(value: Decimal) -> Result<Self, Self::Error> {
Ok(i64::try_from(
value
.value
.checked_div(DECIMAL_PART_POW)
.ok_or(TooLargeForIntegerError)?,
)
.map_err(|_| TooLargeForIntegerError)?
.into())
}
}
impl FromStr for Decimal {
type Err = ParseDecimalError;
fn from_str(input: &str) -> Result<Self, Self::Err> {
let input = input.as_bytes();
if input.is_empty() {
return Err(PARSE_UNEXPECTED_END);
}
let (sign, mut input) = match input.first() {
Some(b'+') => (1_i128, &input[1..]),
Some(b'-') => (-1_i128, &input[1..]),
_ => (1, input),
};
let mut value = 0_i128;
let with_before_dot = input.first().map_or(false, u8::is_ascii_digit);
while let Some(c) = input.first() {
if c.is_ascii_digit() {
value = value
.checked_mul(10)
.ok_or(PARSE_OVERFLOW)?
.checked_add(sign * i128::from(*c - b'0'))
.ok_or(PARSE_OVERFLOW)?;
input = &input[1..];
} else {
break;
}
}
let mut exp = DECIMAL_PART_POW;
if let Some(c) = input.first() {
if *c != b'.' {
return Err(PARSE_UNEXPECTED_CHAR);
}
input = &input[1..];
if input.is_empty() && !with_before_dot {
return Err(PARSE_UNEXPECTED_END);
}
while input.last() == Some(&b'0') {
input = &input[..input.len() - 1];
}
while let Some(c) = input.first() {
if c.is_ascii_digit() {
exp /= 10;
value = value
.checked_mul(10)
.ok_or(PARSE_OVERFLOW)?
.checked_add(sign * i128::from(*c - b'0'))
.ok_or(PARSE_OVERFLOW)?;
input = &input[1..];
} else {
return Err(PARSE_UNEXPECTED_CHAR);
}
}
if exp == 0 {
return Err(PARSE_UNDERFLOW);
}
} else if !with_before_dot {
return Err(PARSE_UNEXPECTED_END);
}
Ok(Self {
value: value.checked_mul(exp).ok_or(PARSE_OVERFLOW)?,
})
}
}
impl fmt::Display for Decimal {
#[allow(clippy::cast_possible_truncation)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.value == 0 {
return if let Some(width) = f.width() {
for _ in 0..width {
f.write_char('0')?;
}
Ok(())
} else {
f.write_char('0')
};
}
let mut value = self.value;
if self.value.is_negative() {
f.write_char('-')?;
}
let mut digits = [b'0'; 40];
let mut i = 0;
while value != 0 {
digits[i] = b'0' + ((value % 10).unsigned_abs() as u8);
value /= 10;
i += 1;
}
let last_non_zero = i - 1;
let first_non_zero = digits
.iter()
.copied()
.enumerate()
.find_map(|(i, v)| if v == b'0' { None } else { Some(i) })
.unwrap_or(40);
let decimal_part_digits = usize::try_from(DECIMAL_PART_DIGITS).map_err(|_| fmt::Error)?;
if last_non_zero >= decimal_part_digits {
let end = if let Some(mut width) = f.width() {
if self.value.is_negative() {
width -= 1;
}
if last_non_zero - decimal_part_digits + 1 < width {
decimal_part_digits + width
} else {
last_non_zero + 1
}
} else {
last_non_zero + 1
};
for c in digits[decimal_part_digits..end].iter().rev() {
f.write_char(char::from(*c))?;
}
} else {
f.write_char('0')?
}
if decimal_part_digits > first_non_zero {
f.write_char('.')?;
let start = if let Some(precision) = f.precision() {
if decimal_part_digits - first_non_zero > precision {
decimal_part_digits - precision
} else {
first_non_zero
}
} else {
first_non_zero
};
for c in digits[start..decimal_part_digits].iter().rev() {
f.write_char(char::from(*c))?;
}
}
Ok(())
}
}
#[derive(Debug, thiserror::Error)]
#[error(transparent)]
pub struct ParseDecimalError(#[from] DecimalParseErrorKind);
#[derive(Debug, Clone, thiserror::Error)]
enum DecimalParseErrorKind {
#[error("Value overflow")]
Overflow,
#[error("Value underflow")]
Underflow,
#[error("Unexpected character")]
UnexpectedChar,
#[error("Unexpected end of string")]
UnexpectedEnd,
}
const PARSE_OVERFLOW: ParseDecimalError = ParseDecimalError(DecimalParseErrorKind::Overflow);
const PARSE_UNDERFLOW: ParseDecimalError = ParseDecimalError(DecimalParseErrorKind::Underflow);
const PARSE_UNEXPECTED_CHAR: ParseDecimalError =
ParseDecimalError(DecimalParseErrorKind::UnexpectedChar);
const PARSE_UNEXPECTED_END: ParseDecimalError =
ParseDecimalError(DecimalParseErrorKind::UnexpectedEnd);
impl From<TooLargeForDecimalError> for ParseDecimalError {
fn from(_: TooLargeForDecimalError) -> Self {
Self(DecimalParseErrorKind::Overflow)
}
}
#[derive(Debug, Clone, Copy, thiserror::Error)]
#[error("Value too large for xsd:decimal internal representation")]
pub struct TooLargeForDecimalError;
#[cfg(test)]
#[allow(clippy::panic_in_result_fn)]
mod tests {
use super::*;
#[test]
fn new() -> Result<(), ParseDecimalError> {
assert_eq!(Decimal::new(1, 0)?.to_string(), "1");
assert_eq!(Decimal::new(1, 1)?.to_string(), "0.1");
assert_eq!(Decimal::new(10, 0)?.to_string(), "10");
assert_eq!(Decimal::new(10, 1)?.to_string(), "1");
assert_eq!(Decimal::new(10, 2)?.to_string(), "0.1");
Ok(())
}
#[test]
fn from_str() -> Result<(), ParseDecimalError> {
Decimal::from_str("").unwrap_err();
Decimal::from_str("+").unwrap_err();
Decimal::from_str("-").unwrap_err();
Decimal::from_str(".").unwrap_err();
Decimal::from_str("+.").unwrap_err();
Decimal::from_str("-.").unwrap_err();
Decimal::from_str("a").unwrap_err();
Decimal::from_str(".a").unwrap_err();
assert_eq!(Decimal::from_str("210")?.to_string(), "210");
assert_eq!(Decimal::from_str("1000")?.to_string(), "1000");
assert_eq!(Decimal::from_str("-1.23")?.to_string(), "-1.23");
assert_eq!(
Decimal::from_str("12678967.543233")?.to_string(),
"12678967.543233"
);
assert_eq!(Decimal::from_str("+100000.00")?.to_string(), "100000");
assert_eq!(Decimal::from_str("0.1220")?.to_string(), "0.122");
assert_eq!(Decimal::from_str(".12200")?.to_string(), "0.122");
assert_eq!(Decimal::from_str("1.")?.to_string(), "1");
assert_eq!(Decimal::from_str("1.0")?.to_string(), "1");
assert_eq!(Decimal::from_str("01.0")?.to_string(), "1");
assert_eq!(Decimal::from_str("0")?.to_string(), "0");
assert_eq!(Decimal::from_str("-0")?.to_string(), "0");
assert_eq!(Decimal::from_str(&Decimal::MAX.to_string())?, Decimal::MAX);
assert_eq!(Decimal::from_str(&Decimal::MIN.to_string())?, Decimal::MIN);
Decimal::from_str("0.0000000000000000001").unwrap_err();
Decimal::from_str("1000000000000000000000").unwrap_err();
assert_eq!(
Decimal::from_str("0.100000000000000000000000000").unwrap(),
Decimal::from_str("0.1").unwrap()
);
Ok(())
}
#[test]
fn format() {
assert_eq!(format!("{}", Decimal::from(0)), "0");
assert_eq!(format!("{}", Decimal::from(1)), "1");
assert_eq!(format!("{}", Decimal::from(10)), "10");
assert_eq!(format!("{}", Decimal::from(100)), "100");
assert_eq!(format!("{}", Decimal::from(-1)), "-1");
assert_eq!(format!("{}", Decimal::from(-10)), "-10");
assert_eq!(format!("{:02}", Decimal::from(0)), "00");
assert_eq!(format!("{:02}", Decimal::from(1)), "01");
assert_eq!(format!("{:02}", Decimal::from(10)), "10");
assert_eq!(format!("{:02}", Decimal::from(100)), "100");
assert_eq!(format!("{:02}", Decimal::from(-1)), "-1");
assert_eq!(format!("{:02}", Decimal::from(-10)), "-10");
}
#[test]
fn add() {
assert!(Decimal::MIN.checked_add(Decimal::STEP).is_some());
assert!(Decimal::MAX.checked_add(Decimal::STEP).is_none());
assert_eq!(
Decimal::MAX.checked_add(Decimal::MIN),
Decimal::STEP.checked_neg()
);
}
#[test]
fn sub() {
assert!(Decimal::MIN.checked_sub(Decimal::STEP).is_none());
assert!(Decimal::MAX.checked_sub(Decimal::STEP).is_some());
}
#[test]
fn mul() -> Result<(), ParseDecimalError> {
assert_eq!(Decimal::from(1).checked_mul(-1), Some(Decimal::from(-1)));
assert_eq!(
Decimal::from(1000).checked_mul(1000),
Some(Decimal::from(1_000_000))
);
assert_eq!(
Decimal::from_str("0.1")?.checked_mul(Decimal::from_str("0.01")?),
Some(Decimal::from_str("0.001")?)
);
assert_eq!(Decimal::from(0).checked_mul(1), Some(Decimal::from(0)));
assert_eq!(Decimal::from(1).checked_mul(0), Some(Decimal::from(0)));
assert_eq!(Decimal::MAX.checked_mul(1), Some(Decimal::MAX));
assert_eq!(Decimal::MIN.checked_mul(1), Some(Decimal::MIN));
assert_eq!(
Decimal::from(1).checked_mul(Decimal::MAX),
Some(Decimal::MAX)
);
assert_eq!(
Decimal::from(1).checked_mul(Decimal::MIN),
Some(Decimal::MIN)
);
assert_eq!(
Decimal::MAX.checked_mul(-1),
Some(Decimal::MIN.checked_add(Decimal::STEP).unwrap())
);
assert_eq!(Decimal::MIN.checked_mul(-1), None);
assert_eq!(
Decimal::MIN
.checked_add(Decimal::STEP)
.unwrap()
.checked_mul(-1),
Some(Decimal::MAX)
);
Ok(())
}
#[test]
fn div() -> Result<(), ParseDecimalError> {
assert_eq!(Decimal::from(1).checked_div(1), Some(Decimal::from(1)));
assert_eq!(Decimal::from(100).checked_div(10), Some(Decimal::from(10)));
assert_eq!(
Decimal::from(10).checked_div(100),
Some(Decimal::from_str("0.1")?)
);
assert_eq!(Decimal::from(1).checked_div(0), None);
assert_eq!(Decimal::from(0).checked_div(1), Some(Decimal::from(0)));
assert_eq!(Decimal::MAX.checked_div(1), Some(Decimal::MAX));
assert_eq!(Decimal::MIN.checked_div(1), Some(Decimal::MIN));
assert_eq!(
Decimal::MAX.checked_div(-1),
Some(Decimal::MIN.checked_add(Decimal::STEP).unwrap())
);
assert_eq!(Decimal::MIN.checked_div(-1), None);
assert_eq!(
Decimal::MIN
.checked_add(Decimal::STEP)
.unwrap()
.checked_div(-1),
Some(Decimal::MAX)
);
Ok(())
}
#[test]
fn rem() -> Result<(), ParseDecimalError> {
assert_eq!(Decimal::from(10).checked_rem(3), Some(Decimal::from(1)));
assert_eq!(Decimal::from(6).checked_rem(-2), Some(Decimal::from(0)));
assert_eq!(
Decimal::from_str("4.5")?.checked_rem(Decimal::from_str("1.2")?),
Some(Decimal::from_str("0.9")?)
);
assert_eq!(Decimal::from(1).checked_rem(0), None);
assert_eq!(
Decimal::MAX.checked_rem(1),
Some(Decimal::from_str("0.687303715884105727")?)
);
assert_eq!(
Decimal::MIN.checked_rem(1),
Some(Decimal::from_str("-0.687303715884105728")?)
);
assert_eq!(
Decimal::MAX.checked_rem(Decimal::STEP),
Some(Decimal::default())
);
assert_eq!(
Decimal::MIN.checked_rem(Decimal::STEP),
Some(Decimal::default())
);
assert_eq!(
Decimal::MAX.checked_rem(Decimal::MAX),
Some(Decimal::default())
);
assert_eq!(
Decimal::MIN.checked_rem(Decimal::MIN),
Some(Decimal::default())
);
Ok(())
}
#[test]
fn round() -> Result<(), ParseDecimalError> {
assert_eq!(Decimal::from(10).checked_round(), Some(Decimal::from(10)));
assert_eq!(Decimal::from(-10).checked_round(), Some(Decimal::from(-10)));
assert_eq!(
Decimal::from(i64::MIN).checked_round(),
Some(Decimal::from(i64::MIN))
);
assert_eq!(
Decimal::from(i64::MAX).checked_round(),
Some(Decimal::from(i64::MAX))
);
assert_eq!(
Decimal::from_str("2.5")?.checked_round(),
Some(Decimal::from(3))
);
assert_eq!(
Decimal::from_str("2.4999")?.checked_round(),
Some(Decimal::from(2))
);
assert_eq!(
Decimal::from_str("-2.5")?.checked_round(),
Some(Decimal::from(-2))
);
assert_eq!(Decimal::MAX.checked_round(), None);
assert_eq!(
Decimal::MAX
.checked_sub(Decimal::from_str("0.5")?)
.unwrap()
.checked_round(),
Some(Decimal::from_str("170141183460469231731")?)
);
assert_eq!(Decimal::MIN.checked_round(), None);
assert_eq!(
Decimal::MIN
.checked_add(Decimal::from_str("0.5")?)
.unwrap()
.checked_round(),
Some(Decimal::from_str("-170141183460469231731")?)
);
Ok(())
}
#[test]
fn ceil() -> Result<(), ParseDecimalError> {
assert_eq!(Decimal::from(10).checked_ceil(), Some(Decimal::from(10)));
assert_eq!(Decimal::from(-10).checked_ceil(), Some(Decimal::from(-10)));
assert_eq!(
Decimal::from_str("10.5")?.checked_ceil(),
Some(Decimal::from(11))
);
assert_eq!(
Decimal::from_str("-10.5")?.checked_ceil(),
Some(Decimal::from(-10))
);
assert_eq!(
Decimal::from(i64::MIN).checked_ceil(),
Some(Decimal::from(i64::MIN))
);
assert_eq!(
Decimal::from(i64::MAX).checked_ceil(),
Some(Decimal::from(i64::MAX))
);
assert_eq!(Decimal::MAX.checked_ceil(), None);
assert_eq!(
Decimal::MAX
.checked_sub(Decimal::from(1))
.unwrap()
.checked_ceil(),
Some(Decimal::from_str("170141183460469231731")?)
);
assert_eq!(
Decimal::MIN.checked_ceil(),
Some(Decimal::from_str("-170141183460469231731")?)
);
Ok(())
}
#[test]
fn floor() -> Result<(), ParseDecimalError> {
assert_eq!(Decimal::from(10).checked_floor(), Some(Decimal::from(10)));
assert_eq!(Decimal::from(-10).checked_floor(), Some(Decimal::from(-10)));
assert_eq!(
Decimal::from_str("10.5")?.checked_floor(),
Some(Decimal::from(10))
);
assert_eq!(
Decimal::from_str("-10.5")?.checked_floor(),
Some(Decimal::from(-11))
);
assert_eq!(
Decimal::from(i64::MIN).checked_floor(),
Some(Decimal::from(i64::MIN))
);
assert_eq!(
Decimal::from(i64::MAX).checked_floor(),
Some(Decimal::from(i64::MAX))
);
assert_eq!(
Decimal::MAX.checked_floor(),
Some(Decimal::from_str("170141183460469231731")?)
);
assert_eq!(Decimal::MIN.checked_floor(), None);
assert_eq!(
Decimal::MIN
.checked_add(Decimal::from_str("1")?)
.unwrap()
.checked_floor(),
Some(Decimal::from_str("-170141183460469231731")?)
);
Ok(())
}
#[test]
fn to_be_bytes() -> Result<(), ParseDecimalError> {
assert_eq!(
Decimal::from_be_bytes(Decimal::MIN.to_be_bytes()),
Decimal::MIN
);
assert_eq!(
Decimal::from_be_bytes(Decimal::MAX.to_be_bytes()),
Decimal::MAX
);
assert_eq!(
Decimal::from_be_bytes(Decimal::from(i64::MIN).to_be_bytes()),
Decimal::from(i64::MIN)
);
assert_eq!(
Decimal::from_be_bytes(Decimal::from(i64::MAX).to_be_bytes()),
Decimal::from(i64::MAX)
);
assert_eq!(
Decimal::from_be_bytes(Decimal::from(0).to_be_bytes()),
Decimal::from(0)
);
assert_eq!(
Decimal::from_be_bytes(Decimal::from(0).to_be_bytes()),
Decimal::from(0)
);
assert_eq!(
Decimal::from_be_bytes(Decimal::from_str("0.01")?.to_be_bytes()),
Decimal::from_str("0.01")?
);
Ok(())
}
#[test]
fn from_bool() {
assert_eq!(Decimal::from(false), Decimal::from(0_u8));
assert_eq!(Decimal::from(true), Decimal::from(1_u8));
}
#[test]
fn from_float() -> Result<(), ParseDecimalError> {
assert_eq!(
Decimal::try_from(Float::from(0.)).ok(),
Some(Decimal::from(0))
);
assert_eq!(
Decimal::try_from(Float::from(-0.)).ok(),
Some(Decimal::from(0))
);
assert_eq!(
Decimal::try_from(Float::from(-123.5)).ok(),
Some(Decimal::from_str("-123.5")?)
);
Decimal::try_from(Float::from(f32::NAN)).unwrap_err();
Decimal::try_from(Float::from(f32::INFINITY)).unwrap_err();
Decimal::try_from(Float::from(f32::NEG_INFINITY)).unwrap_err();
Decimal::try_from(Float::from(f32::MIN)).unwrap_err();
Decimal::try_from(Float::from(f32::MAX)).unwrap_err();
assert!(
Decimal::try_from(Float::from(1_672_507_300_000.))
.unwrap()
.checked_sub(Decimal::from(1_672_507_293_696_i64))
.unwrap()
.checked_abs()
.unwrap()
< Decimal::from(1)
);
Ok(())
}
#[test]
fn from_double() -> Result<(), ParseDecimalError> {
assert_eq!(
Decimal::try_from(Double::from(0.)).ok(),
Some(Decimal::from(0))
);
assert_eq!(
Decimal::try_from(Double::from(-0.)).ok(),
Some(Decimal::from(0))
);
assert_eq!(
Decimal::try_from(Double::from(-123.1)).ok(),
Some(Decimal::from_str("-123.1")?)
);
assert!(
Decimal::try_from(Double::from(1_672_507_302_466.))
.unwrap()
.checked_sub(Decimal::from(1_672_507_302_466_i64))
.unwrap()
.checked_abs()
.unwrap()
< Decimal::from(1)
);
Decimal::try_from(Double::from(f64::NAN)).unwrap_err();
Decimal::try_from(Double::from(f64::INFINITY)).unwrap_err();
Decimal::try_from(Double::from(f64::NEG_INFINITY)).unwrap_err();
Decimal::try_from(Double::from(f64::MIN)).unwrap_err();
Decimal::try_from(Double::from(f64::MAX)).unwrap_err();
Ok(())
}
#[test]
fn to_float() -> Result<(), ParseDecimalError> {
assert_eq!(Float::from(Decimal::from(0)), Float::from(0.));
assert_eq!(Float::from(Decimal::from(1)), Float::from(1.));
assert_eq!(Float::from(Decimal::from(10)), Float::from(10.));
assert_eq!(Float::from(Decimal::from_str("0.1")?), Float::from(0.1));
assert!((Float::from(Decimal::MAX) - Float::from(1.701_412e20)).abs() < Float::from(1.));
assert!((Float::from(Decimal::MIN) - Float::from(-1.701_412e20)).abs() < Float::from(1.));
Ok(())
}
#[test]
fn to_double() -> Result<(), ParseDecimalError> {
assert_eq!(Double::from(Decimal::from(0)), Double::from(0.));
assert_eq!(Double::from(Decimal::from(1)), Double::from(1.));
assert_eq!(Double::from(Decimal::from(10)), Double::from(10.));
assert!(
Double::from(Decimal::from_str("0.1")?) - Double::from(0.1)
< Double::from(f64::from(f32::EPSILON))
);
assert!(
(Double::from(Decimal::MAX) - Double::from(1.701_411_834_604_692_4e20)).abs()
< Double::from(1.)
);
assert!(
(Double::from(Decimal::MIN) - Double::from(-1.701_411_834_604_692_4e20)).abs()
< Double::from(1.)
);
Ok(())
}
#[test]
fn minimally_conformant() -> Result<(), ParseDecimalError> {
assert_eq!(
Decimal::from_str("1234567890123456")?.to_string(),
"1234567890123456"
);
assert_eq!(
Decimal::from_str("-1234567890123456")?.to_string(),
"-1234567890123456"
);
assert_eq!(
Decimal::from_str("0.1234567890123456")?.to_string(),
"0.1234567890123456"
);
assert_eq!(
Decimal::from_str("-0.1234567890123456")?.to_string(),
"-0.1234567890123456"
);
Ok(())
}
}