diff --git a/src/calc.rs b/src/calc.rs index 555994c..427ca96 100644 --- a/src/calc.rs +++ b/src/calc.rs @@ -1,6 +1,6 @@ -use core::{convert::Infallible, fmt::Display}; +use core::{convert::Infallible, fmt::Display, marker::PhantomData}; -use num_traits::{PrimInt, Unsigned}; +use num_traits::{float::FloatCore, PrimInt, Unsigned}; use crate::{Decimal, DecimalError}; @@ -22,16 +22,20 @@ impl Display for StackCalcError { } #[derive(Debug, Clone)] -pub struct StackCalc { - stack: [Decimal; SS], +pub struct StackCalc { + stack: [Decimal; STACK], index: usize, + phantom: PhantomData, } -impl Default for StackCalc { +impl Default + for StackCalc +{ fn default() -> Self { StackCalc { - stack: [Decimal::default(); SS], + stack: [Decimal::default(); STACK], index: 0, + phantom: PhantomData, } } } @@ -48,12 +52,14 @@ impl From for StackCalcError { } } -impl StackCalc { +impl + StackCalc +{ pub fn push( &mut self, - val: impl TryInto, Error: Into>, + val: impl TryInto, Error: Into>, ) -> Result<(), StackCalcError> { - if self.index == SS { + if self.index == STACK { return Err(StackCalcError::StackOverflow); } self.stack[self.index] = val.try_into().map_err(Into::into)?; @@ -61,7 +67,7 @@ impl StackCalc Result, StackCalcError> { + pub fn pop(&mut self) -> Result, StackCalcError> { if self.index == 0 { return Err(StackCalcError::StackUnderflow); } @@ -69,12 +75,12 @@ impl StackCalc Result, StackCalcError> { + pub fn add(&mut self) -> Result, StackCalcError> { let a = self.pop()?; let b = self.pop()?; - let a = f32::try_from(a)?; - let b = f32::try_from(b)?; - let res = Decimal::try_from(a + b)?; + let a = a.to_float::()?; + let b = b.to_float::()?; + let res = Decimal::from_float(a + b)?; self.push(res)?; Ok(res) } @@ -85,7 +91,7 @@ mod tests { use super::*; #[test] fn test_add() -> Result<(), StackCalcError> { - let mut calc = StackCalc::<3, 7>::default(); + let mut calc = StackCalc::::default(); calc.push(Decimal::default())?; calc.push(1337.42)?; assert_eq!(calc.add()?, Decimal::try_from(1337.42)?); diff --git a/src/lib.rs b/src/lib.rs index 508b794..bd59c63 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,7 +3,6 @@ pub mod calc; use core::fmt::Display; -#[cfg(not(test))] use num_traits::float::FloatCore; use num_traits::{one, zero, PrimInt, Unsigned}; @@ -23,30 +22,102 @@ impl Display for DecimalError { } #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct Decimal { +pub struct Decimal { pub minus: bool, - pub significant: [u8; S], + pub significant: [u8; SIZE], pub minus_exponent: bool, - pub exponent: E, + pub exponent: EXP, } -impl Default for Decimal { +impl Decimal { + pub fn from_float(mut f: F) -> Result, DecimalError> { + if !f.is_finite() { + return Err(DecimalError::NotANumber); + } + let minus = f.is_sign_negative(); + if minus { + f = f * F::from(-1).unwrap(); + } + let mut exponent: EXP = zero(); + let minus_exponent = f < one() && f > zero(); + while f > F::from(10).unwrap() { + f = f / F::from(10).unwrap(); + exponent = exponent + .checked_add(&one()) + .ok_or(DecimalError::ExponentOverflow)?; + } + while f < one() { + if f == f * F::from(10).unwrap() { + break; + } + f = f * F::from(10).unwrap(); + exponent = exponent + .checked_add(&one()) + .ok_or(DecimalError::ExponentOverflow)?; + } + let mut significant = [0; SIZE]; + for i in 0..SIZE { + let s = f.trunc().to_u8().unwrap(); + f = f * F::from(10).unwrap(); + f = f - F::from(s * 10).ok_or(DecimalError::ExponentOverflow)?; + debug_assert!(s < 10); + significant[i] = s; + } + Ok(Decimal { + minus, + significant, + minus_exponent, + exponent, + }) + } + + pub fn to_float(&self) -> Result { + let Decimal { + minus, + significant, + minus_exponent, + exponent, + } = *self; + + let mut e = exponent.to_i32().ok_or(DecimalError::ExponentOverflow)?; + if minus_exponent { + e = -e; + } + let e = F::from(10).unwrap().powi(e); + + let mut f: F = zero(); + + for i in (0..SIZE).rev() { + f = f / F::from(10).unwrap(); + let s = significant[i]; + f = f + F::from(s).unwrap() * e; + } + + if minus { + f = f * F::from(-1).unwrap(); + } + + Ok(f) + } +} + +impl Default for Decimal { fn default() -> Self { Decimal { minus: false, - significant: [0; S], + significant: [0; SIZE], minus_exponent: false, exponent: zero(), } } } -impl Display for Decimal { +impl Display for Decimal { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { if self.minus { write!(f, "-")?; } - for i in 0..S { + for i in 0..SIZE { write!(f, "{}", self.significant[i])?; if i == 0 { write!(f, ".")?; @@ -60,80 +131,35 @@ impl Display for Decimal { } } -impl TryFrom for Decimal { +impl TryFrom for Decimal { type Error = DecimalError; - fn try_from(mut f: f32) -> Result { - if !f.is_finite() { - return Err(DecimalError::NotANumber); - } - let minus = f.is_sign_negative(); - if minus { - f *= -1.0; - } - let mut exponent: E = zero(); - let minus_exponent = f < 1.0 && f > 0.0; - while f > 10.0 { - f /= 10.0; - exponent = exponent - .checked_add(&one()) - .ok_or(DecimalError::ExponentOverflow)?; - } - while f < 1.0 { - if f == f * 10.0 { - break; - } - f *= 10.0; - exponent = exponent - .checked_add(&one()) - .ok_or(DecimalError::ExponentOverflow)?; - } - let mut significant = [0; S]; - for i in 0..S { - let s = f as u8; - f *= 10.0; - f -= (s * 10) as f32; - significant[i] = s; - } - Ok(Decimal { - minus, - significant, - minus_exponent, - exponent, - }) + fn try_from(f: f32) -> Result { + Self::from_float(f) } } -impl TryFrom> for f32 { +impl TryFrom for Decimal { type Error = DecimalError; - fn try_from(val: Decimal) -> Result { - let Decimal { - minus, - significant, - minus_exponent, - exponent, - } = val; + fn try_from(f: f64) -> Result { + Self::from_float(f) + } +} - let mut e = exponent.to_i32().ok_or(DecimalError::ExponentOverflow)?; - if minus_exponent { - e = -e; - } - let e = 10.0f32.powi(e); +impl TryFrom> for f32 { + type Error = DecimalError; - let mut f: f32 = 0.0; + fn try_from(val: Decimal) -> Result { + val.to_float() + } +} - for i in (0..S).rev() { - f /= 10.0; - let s = significant[i]; - f += (s as f32) * e; - } +impl TryFrom> for f64 { + type Error = DecimalError; - if minus { - f *= -1.0; - } - - Ok(f) + fn try_from(val: Decimal) -> Result { + val.to_float() } } @@ -189,13 +215,13 @@ mod tests { #[test] fn float_to_decimal_nan() { - let err = Decimal::<7>::try_from(f32::NAN).unwrap_err(); + let err = Decimal::<7>::try_from(f64::NAN).unwrap_err(); assert!(matches!(err, DecimalError::NotANumber)); } #[test] fn float_to_decimal_max() { - let dec = Decimal::<7>::try_from(f32::MAX).unwrap(); + let dec = Decimal::<7>::try_from(f64::MAX).unwrap(); assert!(!dec.minus); assert_eq!(dec.significant, [1, 7, 9, 7, 6, 9, 3]); assert!(!dec.minus_exponent); @@ -204,13 +230,13 @@ mod tests { #[test] fn float_to_decimal_max_overflow() { - let err = Decimal::<7, u8>::try_from(f32::MAX).unwrap_err(); + let err = Decimal::<7, u8>::try_from(f64::MAX).unwrap_err(); assert!(matches!(err, DecimalError::ExponentOverflow)); } #[test] fn float_to_decimal_min() { - let dec = Decimal::<7>::try_from(f32::MIN).unwrap(); + let dec = Decimal::<7>::try_from(f64::MIN).unwrap(); assert!(dec.minus); assert_eq!(dec.significant, [1, 7, 9, 7, 6, 9, 3]); assert!(!dec.minus_exponent); @@ -219,13 +245,13 @@ mod tests { #[test] fn float_to_decimal_min_overflow() { - let err = Decimal::<7, u8>::try_from(f32::MIN).unwrap_err(); + let err = Decimal::<7, u8>::try_from(f64::MIN).unwrap_err(); assert!(matches!(err, DecimalError::ExponentOverflow)); } #[test] fn float_to_decimal_epsilon() { - let dec = Decimal::<7>::try_from(f32::EPSILON).unwrap(); + let dec = Decimal::<7>::try_from(f64::EPSILON).unwrap(); assert!(!dec.minus); assert_eq!(dec.significant, [2, 2, 2, 0, 4, 4, 6]); assert!(dec.minus_exponent); @@ -235,7 +261,7 @@ mod tests { #[test] fn decimal_to_float_zero() { let dec = Decimal::<5, u8>::default(); - assert_eq!(f32::try_from(dec).unwrap(), 0.0); + assert_eq!(f64::try_from(dec).unwrap(), 0.0); } #[test] @@ -243,16 +269,16 @@ mod tests { let mut dec = Decimal::<7, u8>::default(); dec.significant = [1, 3, 3, 7, 4, 2, 0]; dec.exponent = 3; - assert_eq!(f32::try_from(dec).unwrap(), 1337.42); + assert_eq!(f64::try_from(dec).unwrap(), 1337.42); } #[test] fn decimal_to_float_small() { let mut dec = Decimal::<7, u8>::default(); - dec.significant = [1, 3, 3, 7, 4, 2, 0]; + dec.significant = [1, 3, 3, 7, 4, 2, 1]; dec.minus_exponent = true; dec.exponent = 2; - assert_eq!(f32::try_from(dec).unwrap(), 0.0133742); + assert_eq!(f64::try_from(dec).unwrap(), 0.01337421); } #[test] @@ -261,24 +287,24 @@ mod tests { dec.minus = true; dec.significant = [1, 3, 3, 7, 4, 2, 0]; dec.exponent = 3; - assert_eq!(f32::try_from(dec).unwrap(), -1337.42); + assert_eq!(f64::try_from(dec).unwrap(), -1337.42); } #[test] fn decimal_to_float_neg_small() { let mut dec = Decimal::<7, u8>::default(); dec.minus = true; - dec.significant = [1, 3, 3, 7, 4, 2, 0]; + dec.significant = [1, 3, 3, 7, 4, 2, 1]; dec.minus_exponent = true; dec.exponent = 2; - assert_eq!(f32::try_from(dec).unwrap(), -0.0133742); + assert_eq!(f64::try_from(dec).unwrap(), -0.01337421); } #[test] #[ignore = "not working due to precission issues"] fn decimal_to_float_max() { - let dec = Decimal::<16>::try_from(f32::MAX).unwrap(); - assert_eq!(f32::try_from(dec).unwrap(), f32::MAX); + let dec = Decimal::<16>::try_from(f64::MAX).unwrap(); + assert_eq!(f64::try_from(dec).unwrap(), f64::MAX); } #[test]