From 55c2ebadce7a348e6222d89f35e1934f22eccc62 Mon Sep 17 00:00:00 2001 From: Hexa Dust Date: Fri, 18 Jul 2025 21:19:30 +0100 Subject: [PATCH] initial float and decimal --- .gitignore | 1 + Cargo.lock | 7 ++ Cargo.toml | 6 ++ src/lib.rs | 189 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 203 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/lib.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..b090ae4 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "calc-math" +version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..7863318 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "calc-math" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..9ff8e23 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,189 @@ +#![no_std] + +use core::{error::Error, fmt::Display}; + +#[derive(Debug, Clone, Copy)] +pub struct Decimal { + pub minus: bool, + pub significant: [u8; S], + pub minus_exponent: bool, + pub exponent: u8, +} + +#[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"), + } + } +} + +#[derive(Debug, Default, Clone, Copy)] +pub struct Float(f64); + +impl Float { + pub fn add(left: Float, right: Float) -> Float { + Float(left.0 + right.0) + } + + pub fn sub(left: Float, right: Float) -> Float { + Float(left.0 - right.0) + } + + pub fn mul(left: Float, right: Float) -> Float { + Float(left.0 * right.0) + } + + pub fn div(left: Float, right: Float) -> Float { + Float(left.0 / right.0) + } +} + +impl From for Float { + fn from(f: f64) -> Float { + Float(f) + } +} + +impl TryFrom for Decimal { + type Error = DecimalError; + + fn try_from(val: Float) -> Result { + let mut f = val.0; + if !f.is_finite() { + return Err(DecimalError::NotANumber); + } + let minus = f.is_sign_negative(); + if minus { + f *= -1.0; + } + let mut exponent: u8 = 0; + let minus_exponent = f < 1.0 && f > 0.0; + while f > 10.0 { + f /= 10.0; + exponent = exponent + .checked_add(1u8) + .ok_or(DecimalError::ExponentOverflow)?; + } + while f < 1.0 { + if f == f * 10.0 { + break; + } + f *= 10.0; + exponent = exponent + .checked_add(1u8) + .ok_or(DecimalError::ExponentOverflow)?; + } + let mut significant = [0; S]; + for i in 0..S { + let s = f as u8; //TODO: handle NaN etc. + f *= 10.0; + f -= (s * 10) as f64; + significant[i] = s; + } + Ok(Decimal { + minus, + significant, + minus_exponent, + exponent, + }) + } +} + +#[derive(Default)] +pub struct SciNum { + significant: f64, + exponent: i8, +} + +impl SciNum { + pub fn new(significant: f64, exponent: i8) -> SciNum { + let mut out = SciNum { + significant, + exponent, + }; + out.normalize(); + out + } + + pub fn normalize(&mut self) { + while self.significant > 10.0 { + self.significant /= 10.0; + self.exponent = self.exponent.checked_add(1).expect("exponent overflow"); + } + + while self.significant < 0.0 { + self.significant *= 10.0; + self.exponent = self.exponent.checked_sub(1).expect("exponent underflow"); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + SciNum::new(1.2, 10); + } + + #[test] + fn float_to_decimal_zero() { + let dec = Decimal::<5>::try_from(Float::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(Float::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(Float::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(Float::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(Float::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(Float::from(0.0 / 0.0)).unwrap_err(); + assert!(matches!(err, DecimalError::NotANumber)); + } +}