use crate::fpdec_inner::FpdecInner;
use crate::oob_scale_fpdec::OobScaleFpdec;
use crate::{IntoRatioInt, ParseError, Rounding};
use core::{fmt, num::ParseIntError, ops, str::FromStr};
#[allow(unused_imports)]
use num_traits::float::FloatCore; use num_traits::{cast::FromPrimitive, Num, Signed};
#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash, Default)]
#[repr(transparent)]
pub struct ConstScaleFpdec<I, const S: i32>(I);
impl<I, const S: i32> ConstScaleFpdec<I, S>
where
I: FpdecInner,
{
crate::none_scale_common::define_none_scale_common!();
pub const SCALE: i32 = S;
#[must_use]
pub fn checked_mul<J, const S2: i32, const SR: i32>(
self,
rhs: ConstScaleFpdec<J, S2>,
) -> Option<ConstScaleFpdec<I, SR>>
where
J: FpdecInner,
{
self.checked_mul_ext(rhs, Rounding::Round)
}
#[must_use]
pub fn checked_mul_ext<J, const S2: i32, const SR: i32>(
self,
rhs: ConstScaleFpdec<J, S2>,
rounding: Rounding,
) -> Option<ConstScaleFpdec<I, SR>>
where
J: FpdecInner,
{
self.0
.checked_mul_ext(I::from(rhs.0)?, S + S2 - SR, rounding)
.map(ConstScaleFpdec)
}
#[must_use]
pub fn checked_div<J, const S2: i32, const SR: i32>(
self,
rhs: ConstScaleFpdec<J, S2>,
) -> Option<ConstScaleFpdec<I, SR>>
where
J: FpdecInner,
{
self.checked_div_ext(rhs, Rounding::Round)
}
#[must_use]
pub fn checked_div_ext<J, const S2: i32, const SR: i32>(
self,
rhs: ConstScaleFpdec<J, S2>,
rounding: Rounding,
) -> Option<ConstScaleFpdec<I, SR>>
where
J: FpdecInner,
{
self.0
.checked_div_ext(I::from(rhs.0)?, S - S2 - SR, rounding)
.map(ConstScaleFpdec)
}
#[must_use]
pub fn round(self, scale: i32) -> Self {
self.round_ext(scale, Rounding::Round)
}
#[must_use]
pub fn round_ext(self, scale: i32, rounding: Rounding) -> Self {
Self(self.0.round_diff_with_rounding(S - scale, rounding))
}
}
impl<I, const S: i32> ConstScaleFpdec<I, S>
where
I: FpdecInner + Signed,
{
crate::none_scale_common::define_none_scale_common_signed!();
}
impl<I, const S: i32> fmt::Debug for ConstScaleFpdec<I, S>
where
I: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(f, "Fpdec({},{})", self.0, S)
}
}
impl<I, const S: i32> fmt::Display for ConstScaleFpdec<I, S>
where
I: FpdecInner + fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
self.0.display_fmt(S, f)
}
}
impl<I, const S: i32> FromStr for ConstScaleFpdec<I, S>
where
I: FpdecInner + Num<FromStrRadixErr = ParseIntError>,
{
type Err = ParseError;
fn from_str(s: &str) -> Result<Self, ParseError> {
I::try_from_str(s, S).map(Self)
}
}
impl<I, const S: i32> From<OobScaleFpdec<I>> for ConstScaleFpdec<I, S>
where
I: FpdecInner,
{
fn from(od: OobScaleFpdec<I>) -> Self {
Self(od.mantissa())
}
}
macro_rules! convert_from_int {
($from_int_type:ty) => {
impl<I, const S: i32> TryFrom<$from_int_type> for ConstScaleFpdec<I, S>
where
I: FpdecInner,
{
type Error = ParseError;
fn try_from(i: $from_int_type) -> Result<Self, Self::Error> {
if S > 0 {
let i2 = I::from(i).ok_or(ParseError::Overflow)?;
I::checked_from_int(i2, S).map(Self)
} else {
let i2 = i.checked_from_int(S)?;
I::from(i2).ok_or(ParseError::Overflow).map(Self)
}
}
}
};
}
convert_from_int!(i8);
convert_from_int!(i16);
convert_from_int!(i32);
convert_from_int!(i64);
convert_from_int!(i128);
convert_from_int!(u8);
convert_from_int!(u16);
convert_from_int!(u32);
convert_from_int!(u64);
convert_from_int!(u128);
macro_rules! convert_from_float {
($float_type:ty, $from_fn:ident, $to_fn:ident) => {
impl<I, const S: i32> TryFrom<$float_type> for ConstScaleFpdec<I, S>
where
I: FromPrimitive + FpdecInner,
{
type Error = ParseError;
fn try_from(f: $float_type) -> Result<Self, Self::Error> {
let inner_f = if S > 0 {
f * 10.0.powi(S)
} else if S < 0 {
f / 10.0.powi(-S)
} else {
f
};
I::$from_fn(inner_f.round())
.map(Self)
.ok_or(ParseError::Overflow)
}
}
impl<I, const S: i32> From<ConstScaleFpdec<I, S>> for $float_type
where
I: FpdecInner,
{
fn from(dec: ConstScaleFpdec<I, S>) -> Self {
let f = dec.0.$to_fn().unwrap();
if S > 0 {
f / 10.0.powi(S)
} else if S < 0 {
f * 10.0.powi(-S)
} else {
f
}
}
}
};
}
convert_from_float!(f32, from_f32, to_f32);
convert_from_float!(f64, from_f64, to_f64);
impl<I, const S: i32> ops::Neg for ConstScaleFpdec<I, S>
where
I: FpdecInner + Signed,
{
type Output = Self;
fn neg(self) -> Self::Output {
Self(-self.0)
}
}
impl<I, const S: i32> ops::Add for ConstScaleFpdec<I, S>
where
I: FpdecInner,
{
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self(self.0 + rhs.0)
}
}
impl<I, const S: i32> ops::Sub for ConstScaleFpdec<I, S>
where
I: FpdecInner,
{
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Self(self.0 - rhs.0)
}
}
impl<I, J, const S: i32, const S2: i32> ops::Mul<ConstScaleFpdec<J, S2>> for ConstScaleFpdec<I, S>
where
I: FpdecInner,
J: FpdecInner,
{
type Output = Self;
fn mul(self, rhs: ConstScaleFpdec<J, S2>) -> Self::Output {
self.checked_mul(rhs)
.expect("overflow in decimal multiplication")
}
}
impl<I, J, const S: i32> ops::Mul<J> for ConstScaleFpdec<I, S>
where
I: FpdecInner,
J: Into<I> + Num, {
type Output = Self;
fn mul(self, rhs: J) -> Self::Output {
self.checked_mul_int(rhs)
.expect("overflow in decimal multiplication")
}
}
impl<I, J, const S: i32, const S2: i32> ops::Div<ConstScaleFpdec<J, S2>> for ConstScaleFpdec<I, S>
where
I: FpdecInner,
J: FpdecInner,
{
type Output = Self;
fn div(self, rhs: ConstScaleFpdec<J, S2>) -> Self::Output {
self.checked_div(rhs).expect("fail in decimal division")
}
}
impl<I, J, const S: i32> ops::Div<J> for ConstScaleFpdec<I, S>
where
I: FpdecInner,
J: Into<I> + Num,
{
type Output = Self;
fn div(self, rhs: J) -> Self::Output {
self.checked_div_int(rhs).expect("fail in decimal division")
}
}
impl<I, const S: i32> ops::AddAssign for ConstScaleFpdec<I, S>
where
I: FpdecInner,
{
fn add_assign(&mut self, rhs: Self) {
self.0 += rhs.0;
}
}
impl<I, const S: i32> ops::SubAssign for ConstScaleFpdec<I, S>
where
I: FpdecInner,
{
fn sub_assign(&mut self, rhs: Self) {
self.0 -= rhs.0;
}
}
impl<I, J, const S: i32, const S2: i32> ops::MulAssign<ConstScaleFpdec<J, S2>>
for ConstScaleFpdec<I, S>
where
I: FpdecInner,
J: FpdecInner,
{
fn mul_assign(&mut self, rhs: ConstScaleFpdec<J, S2>) {
*self = *self * rhs;
}
}
impl<I, J, const S: i32> ops::MulAssign<J> for ConstScaleFpdec<I, S>
where
I: FpdecInner,
J: Into<I> + Num,
{
fn mul_assign(&mut self, rhs: J) {
*self = *self * rhs;
}
}
impl<I, J, const S: i32, const S2: i32> ops::DivAssign<ConstScaleFpdec<J, S2>>
for ConstScaleFpdec<I, S>
where
I: FpdecInner,
J: FpdecInner,
{
fn div_assign(&mut self, rhs: ConstScaleFpdec<J, S2>) {
*self = *self / rhs;
}
}
impl<I, J, const S: i32> ops::DivAssign<J> for ConstScaleFpdec<I, S>
where
I: FpdecInner,
J: Into<I> + Num,
{
fn div_assign(&mut self, rhs: J) {
*self = *self / rhs;
}
}
impl<I, const S: i32> core::iter::Sum for ConstScaleFpdec<I, S>
where
I: FpdecInner,
{
fn sum<Iter: Iterator<Item = Self>>(iter: Iter) -> Self {
iter.fold(Self::ZERO, |acc, d| acc + d)
}
}
impl<'a, I, const S: i32> core::iter::Sum<&'a Self> for ConstScaleFpdec<I, S>
where
I: FpdecInner,
{
fn sum<Iter: Iterator<Item = &'a Self>>(iter: Iter) -> Self {
iter.fold(Self::ZERO, |acc, d| acc + *d)
}
}
impl<I, J, const S: i32> IntoRatioInt<J> for ConstScaleFpdec<I, S>
where
I: FpdecInner + Into<J>,
{
fn to_int(self) -> J {
self.mantissa().into()
}
}
#[cfg(feature = "serde")]
use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[cfg(feature = "serde")]
impl<I, const S0: i32> Serialize for ConstScaleFpdec<I, S0>
where
I: FpdecInner + fmt::Display,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
if serializer.is_human_readable() {
serializer.collect_str(self)
} else {
Into::<f64>::into(*self).serialize(serializer)
}
}
}
#[cfg(feature = "serde")]
impl<'de, I, const S: i32> Deserialize<'de> for ConstScaleFpdec<I, S>
where
I: FromPrimitive + FpdecInner + Num<FromStrRadixErr = ParseIntError>,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
use core::marker::PhantomData;
use core::str::FromStr;
use serde::de::{self, Visitor};
struct ConstScaleFpdecVistor<I, const S: i32>(PhantomData<I>);
macro_rules! visit_num {
($func_name:ident, $num_type:ty) => {
fn $func_name<E: de::Error>(self, n: $num_type) -> Result<Self::Value, E> {
ConstScaleFpdec::try_from(n).map_err(|_| E::custom("decimal overflow"))
}
};
}
impl<'de, I, const S: i32> Visitor<'de> for ConstScaleFpdecVistor<I, S>
where
I: FromPrimitive + FpdecInner + Num<FromStrRadixErr = ParseIntError>,
{
type Value = ConstScaleFpdec<I, S>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "decimal")
}
fn visit_str<E: de::Error>(self, s: &str) -> Result<Self::Value, E> {
ConstScaleFpdec::from_str(s).map_err(E::custom)
}
visit_num!(visit_f32, f32);
visit_num!(visit_f64, f64);
visit_num!(visit_i8, i8);
visit_num!(visit_i16, i16);
visit_num!(visit_i32, i32);
visit_num!(visit_i64, i64);
visit_num!(visit_i128, i128);
visit_num!(visit_u8, u8);
visit_num!(visit_u16, u16);
visit_num!(visit_u32, u32);
visit_num!(visit_u64, u64);
visit_num!(visit_u128, u128);
}
deserializer.deserialize_any(ConstScaleFpdecVistor(PhantomData))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate as primitive_fixed_point_decimal;
use crate::fpdec;
#[test]
fn test_mul() {
let two_p12: ConstScaleFpdec<i32, 12> = fpdec!(2e-12);
let two_n12: ConstScaleFpdec<i32, -12> = fpdec!(2e+12);
let two_p6: ConstScaleFpdec<i32, 6> = fpdec!(2e-6);
let two_n6: ConstScaleFpdec<i32, -6> = fpdec!(2e+6);
let two_p3: ConstScaleFpdec<i32, 3> = fpdec!(2e-3);
let two_n3: ConstScaleFpdec<i32, -3> = fpdec!(2e+3);
let two_0: ConstScaleFpdec<i32, 0> = fpdec!(2);
let zero_p6 = ConstScaleFpdec::<i32, 6>::ZERO;
let zero_n6 = ConstScaleFpdec::<i32, -6>::ZERO;
let four_p12: ConstScaleFpdec<i32, 12> = fpdec!(4e-12);
let four_n12: ConstScaleFpdec<i32, -12> = fpdec!(4e+12);
let four_p6: ConstScaleFpdec<i32, 6> = fpdec!(4e-6);
let four_n6: ConstScaleFpdec<i32, -6> = fpdec!(4e+6);
let four_p3: ConstScaleFpdec<i32, 3> = fpdec!(4e-3);
let four_n3: ConstScaleFpdec<i32, -3> = fpdec!(4e+3);
let four_0: ConstScaleFpdec<i32, 0> = fpdec!(4);
assert_eq!(two_p12.mantissa(), 2);
assert_eq!(two_n12.mantissa(), 2);
assert_eq!(two_p6.mantissa(), 2);
assert_eq!(two_n6.mantissa(), 2);
assert_eq!(two_p3.mantissa(), 2);
assert_eq!(two_n3.mantissa(), 2);
assert_eq!(two_0.mantissa(), 2);
assert_eq!(zero_p6.mantissa(), 0);
assert_eq!(zero_n6.mantissa(), 0);
assert_eq!(two_p3.checked_mul(two_p3), Some(four_p6));
assert_eq!(two_n3.checked_mul(two_n3), Some(four_n6));
assert_eq!(two_p6.checked_mul(two_p6), Some(four_p12));
assert_eq!(two_n6.checked_mul(two_n6), Some(four_n12));
assert_eq!(two_0.checked_mul(two_p6), Some(four_p6));
assert_eq!(two_0.checked_mul(two_n6), Some(four_n6));
assert_eq!(two_n6.checked_mul(two_p6), Some(four_0));
assert_eq!(two_p6.checked_mul(two_p6), Some(zero_p6));
assert_eq!(two_p6.checked_mul(two_p3), Some(zero_p6));
assert_eq!(two_n6.checked_mul(two_p3), Some(zero_n6));
assert_eq!(two_n12.checked_mul(two_p12), Some(zero_n6));
assert_eq!(two_p6.checked_mul(two_p3), four_p12.checked_mul_int(1000));
assert_eq!(two_p6.checked_mul(two_0), four_p12.checked_mul_int(1000000));
assert_eq!(two_n6.checked_mul(two_n3), four_n3.checked_mul_int(1000000));
assert_eq!(two_n6.checked_mul(two_p3), four_p3.checked_mul_int(1000000));
assert_eq!(two_p6.checked_mul(two_n3), four_p6.checked_mul_int(1000));
assert_eq!(two_n12.checked_mul(two_p12), four_p3.checked_mul_int(1000));
assert_eq!(two_p6.checked_mul::<_, 6, 0>(two_p6), None);
assert_eq!(two_p12.checked_mul::<_, 6, 6>(two_p6), None);
assert_eq!(two_p6.checked_mul::<_, -6, -10>(two_n6), None);
assert_eq!(two_n6.checked_mul::<_, -6, 0>(two_n6), None);
assert_eq!(two_n12.checked_mul::<_, -6, -6>(two_n6), None);
assert_eq!(two_n6.checked_mul::<_, 6, 10>(two_p6), None);
}
#[test]
fn test_mul_overflow() {
let max_p6 = ConstScaleFpdec::<i32, 6>::MAX;
let min_p6 = ConstScaleFpdec::<i32, 6>::MIN;
let ten_p6: ConstScaleFpdec<i32, 6> = fpdec!(10);
let half_min_p6 = ConstScaleFpdec::<i32, 6>::MIN.checked_div_int(2).unwrap();
let half_max_p6 = ConstScaleFpdec::<i32, 6>::MAX
.checked_div_int_ext(2, Rounding::Floor)
.unwrap();
let max_p5 = ConstScaleFpdec::<i32, 5>::MAX;
let min_p5 = ConstScaleFpdec::<i32, 5>::MIN;
assert_eq!(max_p6.checked_mul_int(2), None);
assert_eq!(min_p6.checked_mul_int(2), None);
assert_eq!(half_min_p6.checked_mul_int(2), Some(min_p6));
assert_eq!(
half_max_p6.checked_mul_int(2),
max_p6.checked_sub(fpdec!(1e-6))
);
assert_eq!(max_p6.checked_mul(ten_p6), Some(max_p5));
assert_eq!(min_p6.checked_mul(ten_p6), Some(min_p5));
assert_eq!(max_p6.checked_mul::<_, 6, 6>(max_p6), None);
assert_eq!(max_p6.checked_mul::<_, 6, 6>(ten_p6), None);
assert_eq!(half_max_p6.checked_mul::<_, 6, 6>(ten_p6), None);
assert_eq!(min_p6.checked_mul::<_, 6, 6>(min_p6), None);
assert_eq!(min_p6.checked_mul::<_, 6, 6>(ten_p6), None);
assert_eq!(half_min_p6.checked_mul::<_, 6, 6>(ten_p6), None);
assert_eq!(max_p6.checked_mul::<_, 6, 2>(max_p6), None);
assert_eq!(ten_p6.checked_mul::<_, 6, 2>(ten_p6), None);
assert_eq!(max_p6.checked_mul::<_, 6, -22>(max_p6), None);
assert_eq!(ten_p6.checked_mul::<_, 6, -22>(ten_p6), None);
}
#[test]
fn test_div() {
let two_p12: ConstScaleFpdec<i32, 12> = fpdec!(2e-12);
let two_n12: ConstScaleFpdec<i32, -12> = fpdec!(2e+12);
let two_p6: ConstScaleFpdec<i32, 6> = fpdec!(2e-6);
let two_n6: ConstScaleFpdec<i32, -6> = fpdec!(2e+6);
let two_p3: ConstScaleFpdec<i32, 3> = fpdec!(2e-3);
let two_n3: ConstScaleFpdec<i32, -3> = fpdec!(2e+3);
let two_0: ConstScaleFpdec<i32, 0> = fpdec!(2);
let zero_p6 = ConstScaleFpdec::<i32, 6>::ZERO;
let zero_n6 = ConstScaleFpdec::<i32, -6>::ZERO;
let four_p12: ConstScaleFpdec<i32, 12> = fpdec!(4e-12);
let four_n12: ConstScaleFpdec<i32, -12> = fpdec!(4e+12);
let four_p6: ConstScaleFpdec<i32, 6> = fpdec!(4e-6);
let four_n6: ConstScaleFpdec<i32, -6> = fpdec!(4e+6);
let four_p3: ConstScaleFpdec<i32, 3> = fpdec!(4e-3);
let four_n3: ConstScaleFpdec<i32, -3> = fpdec!(4e+3);
let four_0: ConstScaleFpdec<i32, 0> = fpdec!(4);
assert_eq!(four_p3.checked_div(two_p3), Some(two_0));
assert_eq!(four_n3.checked_div(two_n3), Some(two_0));
assert_eq!(four_p12.checked_div(two_p6), Some(two_p6));
assert_eq!(four_n6.checked_div(two_n12), Some(two_p6));
assert_eq!(four_0.checked_div(two_p6), Some(two_n6));
assert_eq!(four_0.checked_div(two_n6), Some(two_p6));
assert_eq!(four_n6.checked_div(two_p6), Some(two_n12));
assert_eq!(four_p6.checked_div(two_p6), Some(zero_n6));
assert_eq!(four_p12.checked_div(two_p3), Some(zero_p6));
assert_eq!(four_p6.checked_div(two_n3), Some(zero_p6));
assert_eq!(four_p6.checked_div(two_p3), two_p6.checked_mul_int(1000));
assert_eq!(four_p6.checked_div(two_0), two_p12.checked_mul_int(1000000));
assert_eq!(four_n6.checked_div(two_n3), two_0.checked_mul_int(1000));
assert_eq!(four_n6.checked_div(two_p3), two_n6.checked_mul_int(1000));
assert_eq!(four_p6.checked_div(two_n3), two_p12.checked_mul_int(1000));
assert_eq!(four_p6.checked_div::<_, 6, -10>(two_p6), None);
assert_eq!(four_p12.checked_div::<_, 6, -6>(two_p6), None);
assert_eq!(four_p6.checked_div::<_, -6, 0>(two_n6), None);
assert_eq!(four_n6.checked_div::<_, -6, 10>(two_n6), None);
assert_eq!(four_n12.checked_div::<_, -6, 6>(two_n6), None);
assert_eq!(four_n6.checked_div::<_, 6, 0>(two_p6), None);
}
#[test]
fn test_div_overflow() {
let max_p6 = ConstScaleFpdec::<i32, 6>::MAX;
let min_p6 = ConstScaleFpdec::<i32, 6>::MIN;
let cent_p6: ConstScaleFpdec<i32, 6> = fpdec!(0.1);
let half_min_p6 = ConstScaleFpdec::<i32, 6>::MIN.checked_div_int(2).unwrap();
let half_max_p6 = ConstScaleFpdec::<i32, 6>::MAX
.checked_div_int_ext(2, Rounding::Floor)
.unwrap();
let max_p5 = ConstScaleFpdec::<i32, 5>::MAX;
let min_p5 = ConstScaleFpdec::<i32, 5>::MIN;
assert_eq!(max_p6.checked_div(cent_p6), Some(max_p5));
assert_eq!(min_p6.checked_div(cent_p6), Some(min_p5));
assert_eq!(max_p6.checked_div::<_, 6, 6>(cent_p6), None);
assert_eq!(half_max_p6.checked_div::<_, 6, 6>(cent_p6), None);
assert_eq!(min_p6.checked_div::<_, 6, 6>(cent_p6), None);
assert_eq!(half_min_p6.checked_div::<_, 6, 6>(cent_p6), None);
assert_eq!(max_p6.checked_div::<_, 6, 10>(max_p6), None);
assert_eq!(cent_p6.checked_div::<_, 6, 10>(cent_p6), None);
assert_eq!(max_p6.checked_div::<_, 6, -10>(max_p6), None);
assert_eq!(cent_p6.checked_div::<_, 6, -10>(cent_p6), None);
}
type Dec32p2 = ConstScaleFpdec<i32, 2>;
type Dec32n2 = ConstScaleFpdec<i32, -2>;
#[test]
fn test_from_int() {
assert_eq!(Dec32p2::try_from(1_i16).unwrap().mantissa(), 100);
assert_eq!(Dec32p2::try_from(i32::MAX), Err(ParseError::Overflow));
assert_eq!(
Dec32p2::try_from(i16::MAX).unwrap().mantissa(),
i16::MAX as i32 * 100
);
assert_eq!(
Dec32n2::try_from(i32::MAX as i64 * 100).unwrap().mantissa(),
i32::MAX
);
assert_eq!(Dec32p2::try_from(i32::MAX), Err(ParseError::Overflow));
assert_eq!(
Dec32n2::try_from(i32::MAX as i64 * 1000),
Err(ParseError::Overflow)
);
}
#[test]
fn test_from_float() {
assert_eq!(Dec32p2::try_from(3.1415).unwrap().mantissa(), 314);
assert_eq!(Dec32n2::try_from(31415.16).unwrap().mantissa(), 314);
assert_eq!(Dec32p2::try_from(3.14e10), Err(ParseError::Overflow));
assert_eq!(Dec32n2::try_from(3.14e16), Err(ParseError::Overflow));
}
#[test]
fn test_fmt() {
assert_eq!(Dec32p2::from_str("0"), Ok(fpdec!(0)));
assert_eq!(Dec32p2::from_str("1000"), Ok(fpdec!(1000)));
assert_eq!(Dec32p2::from_str("-1000"), Ok(fpdec!(-1000)));
assert_eq!(Dec32p2::from_str("0.12"), Ok(fpdec!(0.12)));
assert_eq!(Dec32p2::from_str("-0.12"), Ok(fpdec!(-0.12)));
assert_eq!(Dec32p2::from_str("3.14"), Ok(fpdec!(3.14)));
assert_eq!(Dec32p2::from_str("-3.14"), Ok(fpdec!(-3.14)));
assert_eq!(Dec32p2::from_str("3.1415"), Err(ParseError::Precision));
assert_eq!(Dec32n2::from_str("1000"), Ok(fpdec!(1000)));
assert_eq!(Dec32n2::from_str("-1000"), Ok(fpdec!(-1000)));
assert_eq!(Dec32n2::from_str("1000.00"), Err(ParseError::Precision));
assert_eq!(Dec32n2::from_str("1001"), Err(ParseError::Precision));
}
struct Buffer {
data: [u8; 100],
len: usize,
}
impl Buffer {
fn new() -> Self {
Buffer {
data: [0; 100],
len: 0,
}
}
fn as_str(&self) -> &str {
unsafe { core::str::from_utf8_unchecked(&self.data[..self.len]) }
}
}
use core::fmt::Write;
impl Write for Buffer {
fn write_str(&mut self, s: &str) -> fmt::Result {
let bytes = s.as_bytes();
if self.len + bytes.len() > self.data.len() {
return Err(fmt::Error);
}
self.data[self.len..self.len + bytes.len()].copy_from_slice(bytes);
self.len += bytes.len();
Ok(())
}
}
type Dec64p7 = ConstScaleFpdec<i64, 7>;
fn do_check_fmt(f: f64) {
let mut buf = Buffer::new();
let dec: Dec64p7 = fpdec!(f);
write!(buf, "{dec}").unwrap();
let dec2: Dec64p7 = Dec64p7::from_str(buf.as_str()).unwrap();
assert_eq!(dec, dec2);
let mut buf = Buffer::new();
let dec: Dec64p7 = fpdec!(-f);
write!(buf, "{dec:.7}").unwrap();
let dec2: Dec64p7 = Dec64p7::from_str(buf.as_str()).unwrap();
assert_eq!(dec, dec2);
let mut buf = Buffer::new();
let dec: Dec64p7 = fpdec!(-f);
write!(buf, "{dec}").unwrap();
let dec2: Dec64p7 = Dec64p7::from_str(buf.as_str()).unwrap();
assert_eq!(dec, dec2);
}
#[test]
fn test_fmt2() {
do_check_fmt(0.0);
do_check_fmt(1.0);
do_check_fmt(123.0);
do_check_fmt(123.456);
do_check_fmt(123.4567);
do_check_fmt(123.002);
do_check_fmt(123.00002);
do_check_fmt(123.0000002);
do_check_fmt(0.456);
do_check_fmt(0.4567);
do_check_fmt(0.002);
do_check_fmt(0.00002);
do_check_fmt(0.0000002);
}
#[test]
fn test_sum() {
let v: [Dec32p2; 3] = [fpdec!(0.1), fpdec!(0.2), fpdec!(0.3)];
let s: Dec32p2 = v.iter().sum();
assert_eq!(s, fpdec!(0.6));
}
}