#![no_std] use core::{error::Error, fmt::Display}; #[cfg(not(test))] use num_traits::float::FloatCore; use num_traits::{one, zero, PrimInt, Unsigned}; #[derive(Debug, Clone, Copy)] pub struct Decimal { pub minus: bool, pub significant: [u8; S], pub minus_exponent: bool, pub exponent: E, } impl Default for Decimal { fn default() -> Self { Decimal { minus: false, significant: [0; S], minus_exponent: false, exponent: zero(), } } } #[derive(Debug)] pub enum DecimalError { ExponentOverflow, NotANumber, } impl Error for DecimalError {} impl Display for DecimalError { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { DecimalError::ExponentOverflow => f.write_str("Exponent overflow"), DecimalError::NotANumber => f.write_str("Not a number"), } } } impl TryFrom for Decimal { type Error = DecimalError; fn try_from(mut f: f64) -> 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 f64; significant[i] = s; } Ok(Decimal { minus, significant, minus_exponent, exponent, }) } } impl TryFrom> for f64 { type Error = DecimalError; fn try_from(val: Decimal) -> Result { let Decimal { minus, significant, minus_exponent, exponent, } = val; let mut f: f64 = 0.0; let e = if minus_exponent { 10.0f64.powi(-exponent.to_i32().ok_or(DecimalError::ExponentOverflow)?) } else { 10.0f64.powi(exponent.to_i32().ok_or(DecimalError::ExponentOverflow)?) }; for i in (0..S).rev() { f /= 10.0; let s = significant[i]; f += (s as f64) * e; } if minus { f *= -1.0; } Ok(f) } } #[cfg(test)] mod tests { use super::*; #[test] fn float_to_decimal_zero() { let dec = Decimal::<5>::try_from(0.0).unwrap(); assert!(!dec.minus); assert_eq!(dec.significant, [0, 0, 0, 0, 0]); assert!(!dec.minus_exponent); assert_eq!(dec.exponent, 0) } #[test] fn float_to_decimal_pos_big() { let dec = Decimal::<7>::try_from(1337.42).unwrap(); assert!(!dec.minus); assert_eq!(dec.significant, [1, 3, 3, 7, 4, 2, 0]); assert!(!dec.minus_exponent); assert_eq!(dec.exponent, 3) } #[test] fn float_to_decimal_pos_small() { let dec = Decimal::<7>::try_from(0.0133742).unwrap(); assert!(!dec.minus); assert_eq!(dec.significant, [1, 3, 3, 7, 4, 2, 0]); assert!(dec.minus_exponent); assert_eq!(dec.exponent, 2) } #[test] fn float_to_decimal_neg_big() { let dec = Decimal::<7>::try_from(-1337.42).unwrap(); assert!(dec.minus); assert_eq!(dec.significant, [1, 3, 3, 7, 4, 2, 0]); assert!(!dec.minus_exponent); assert_eq!(dec.exponent, 3) } #[test] fn float_to_decimal_neg_small() { let dec = Decimal::<7>::try_from(-0.0133742).unwrap(); assert!(dec.minus); assert_eq!(dec.significant, [1, 3, 3, 7, 4, 2, 0]); assert!(dec.minus_exponent); assert_eq!(dec.exponent, 2) } #[test] fn float_to_decimal_nan() { 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(f64::MAX).unwrap(); assert!(!dec.minus); assert_eq!(dec.significant, [1, 7, 9, 7, 6, 9, 3]); assert!(!dec.minus_exponent); assert_eq!(dec.exponent, 308) } #[test] fn float_to_decimal_max_overflow() { 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(f64::MIN).unwrap(); assert!(dec.minus); assert_eq!(dec.significant, [1, 7, 9, 7, 6, 9, 3]); assert!(!dec.minus_exponent); assert_eq!(dec.exponent, 308) } #[test] fn float_to_decimal_min_overflow() { 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(f64::EPSILON).unwrap(); assert!(!dec.minus); assert_eq!(dec.significant, [2, 2, 2, 0, 4, 4, 6]); assert!(dec.minus_exponent); assert_eq!(dec.exponent, 16) } #[test] fn decimal_to_float_zero() { let dec = Decimal::<5, u8>::default(); assert_eq!(f64::try_from(dec).unwrap(), 0.0); } #[test] fn decimal_to_float_big() { let mut dec = Decimal::<7, u8>::default(); dec.significant = [1, 3, 3, 7, 4, 2, 0]; dec.exponent = 3; 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.minus_exponent = true; dec.exponent = 2; assert_eq!(f64::try_from(dec).unwrap(), 0.0133742); } #[test] fn decimal_to_float_neg_big() { let mut dec = Decimal::<7, u8>::default(); dec.minus = true; dec.significant = [1, 3, 3, 7, 4, 2, 0]; dec.exponent = 3; 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.minus_exponent = true; dec.exponent = 2; assert_eq!(f64::try_from(dec).unwrap(), -0.0133742); } // #[test] // fn decimal_to_float_max() { // let dec = Decimal::<16>::try_from(f64::MAX / 2.0).unwrap(); // assert_eq!(f64::try_from(dec).unwrap(), f64::MAX / 2.0); // } }