use std::ops::{Add,Sub,Mul,Div,AddAssign,SubAssign,MulAssign,DivAssign,Neg,Rem};
use crate::ParsingStandardFormError;
#[derive(Clone,PartialEq)]
pub struct StandardForm {
mantissa : f64,
exponent : i8
}
impl StandardForm {
pub fn new(mantissa : f64,exponent : i8) -> Self {
let mut instance = Self::new_unchecked(mantissa,exponent);
instance.adjust();
instance
}
pub(crate) fn new_unchecked(mantissa : f64,exponent : i8) -> Self {
Self { mantissa , exponent }
}
fn in_range(&self) -> bool {
(self.mantissa >= 1.0 && self.mantissa <= 10.0) || (self.mantissa >= -10.0 && self.mantissa <= -1.0)
}
fn adjust(&mut self) {
if self.in_range() || self.mantissa == 0.0 {
return;
}
match self.mantissa > -1.0 && self.mantissa < 1.0 {
true => while !self.in_range() {
self.mantissa *= 10.0;
self.exponent -= 1;
},
false => while !self.in_range() {
self.mantissa /= 10.0;
self.exponent += 1;
}
}
}
}
impl StandardForm {
pub const fn mantissa(&self) -> &f64 {
&self.mantissa
}
pub const fn exponent(&self) -> &i8 {
&self.exponent
}
}
impl StandardForm {
pub fn to_scientific_notation(&self) -> String {
format!("{}e{}", self.mantissa, self.exponent)
}
pub fn to_engineering_notation(&self) -> String {
format!("{}*10^{}", self.mantissa, self.exponent)
}
}
impl Default for StandardForm {
fn default() -> Self {
Self { mantissa : 1.0, exponent : 0 }
}
}
impl PartialOrd for StandardForm {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
match self.exponent == other.exponent {
true => self.mantissa.partial_cmp(&other.mantissa),
false => self.exponent.partial_cmp(&other.exponent)
}
}
}
impl std::fmt::Display for StandardForm {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
if self.exponent > 4 {
return write!(f,"{}",self.to_scientific_notation());
};
write!(f,"{}",self.mantissa * 10_i32.pow(self.exponent as u32) as f64)
}
}
impl std::fmt::Debug for StandardForm {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f,"{}",self.to_string())
}
}
impl TryFrom<&str> for StandardForm {
type Error = ParsingStandardFormError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
if let Ok(number) = value.parse::<f64>() {
return Ok(number.into());
}
if let Some(index) = value.find('e') {
let m_str : f64 = value[0..index].parse()?;
let e_str : i8 = value[index + 1..].parse()?;
return Ok(StandardForm::new(m_str,e_str));
}
if let Some(index) = value.find('^') {
let m_str : f64 = value[0..index - 2].parse()?;
let e_str : i8 = value[index + 1..].parse()?;
return Ok(StandardForm::new(m_str,e_str));
}
Err(ParsingStandardFormError::InvalidFormat)
}
}
impl TryFrom<&[u8]> for StandardForm {
type Error = ParsingStandardFormError;
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
Self::try_from(std::str::from_utf8(value)?)
}
}
impl Neg for StandardForm {
type Output = Self;
fn neg(self) -> Self::Output {
Self::new_unchecked(-self.mantissa,self.exponent)
}
}
impl Rem for StandardForm {
type Output = Self;
fn rem(self,other : Self) -> Self::Output {
self.clone() - (self / other.clone() * other)
}
}
impl Add for StandardForm {
type Output = Self;
fn add(self, other: Self) -> Self {
let max_power = self.exponent.max(other.exponent);
let num_sum = self.mantissa * 10.0_f64.powf((self.exponent - max_power) as f64) + other.mantissa * 10.0_f64.powf((other.exponent - max_power) as f64);
StandardForm::new(num_sum, max_power)
}
}
impl AddAssign for StandardForm {
fn add_assign(&mut self, other: Self) {
let max_power = self.exponent.max(other.exponent);
let num_sum = self.mantissa * 10.0_f64.powf((self.exponent - max_power) as f64) + other.mantissa * 10.0_f64.powf((other.exponent - max_power) as f64);
self.mantissa = num_sum;
self.exponent = max_power;
self.adjust();
}
}
impl Sub for StandardForm {
type Output = Self;
fn sub(self, other: Self) -> Self {
let min = self.exponent.min(other.exponent);
let x = self.mantissa * 10_i32.pow((self.exponent - min) as u32) as f64;
let y = other.mantissa * 10_i32.pow((other.exponent - min) as u32) as f64;
let result = x - y;
let rounded = (result * 1.0e6).round() / 1.0e6;
StandardForm::new(rounded,min)
}
}
impl SubAssign for StandardForm {
fn sub_assign(&mut self, other: Self) {
let min = self.exponent.min(other.exponent);
let x = self.mantissa * 10_i32.pow((self.exponent - min) as u32) as f64;
let y = other.mantissa * 10_i32.pow((other.exponent - min) as u32) as f64;
let result = x - y;
let rounded = (result * 1.0e6).round() / 1.0e6;
self.mantissa = rounded;
self.exponent = min;
self.adjust();
}
}
impl Mul for StandardForm {
type Output = Self;
fn mul(self, other: Self) -> Self {
let exponent = self.exponent + other.exponent;
let mantissa = self.mantissa * other.mantissa;
let rounded = (mantissa * 1.0e6).round() / 1.0e6;
StandardForm::new(rounded,exponent)
}
}
impl MulAssign for StandardForm {
fn mul_assign(&mut self, other: Self) {
let exponent = self.exponent + other.exponent;
let mantissa = self.mantissa * other.mantissa;
let rounded = (mantissa * 1.0e6).round() / 1.0e6;
self.mantissa = rounded;
self.exponent = exponent;
self.adjust();
}
}
impl Div for StandardForm {
type Output = Self;
fn div(self, other: Self) -> Self {
StandardForm::new(self.mantissa / other.mantissa,self.exponent - other.exponent)
}
}
impl DivAssign for StandardForm {
fn div_assign(&mut self, other: Self) {
self.mantissa /= other.mantissa;
self.exponent /= other.exponent;
self.adjust();
}
}
macro_rules! primitives {
(form => $($t:ty),*) => {
$(
impl From<$t> for StandardForm {
fn from(value: $t) -> Self {
StandardForm::new(value as f64,0)
}
}
)*
};
(eq => $($t:ty),*) => {
$(
impl PartialEq<$t> for StandardForm {
fn eq(&self,other: &$t) -> bool {
let rhs : Self = (*other).into();
*self == rhs
}
}
)*
};
(ord => $($t:ty),*) => {
$(
impl PartialOrd<$t> for StandardForm {
fn partial_cmp(&self, other: &$t) -> Option<std::cmp::Ordering> {
let rhs : Self = (*other).into();
self.partial_cmp(&rhs)
}
}
)*
};
(add => $($t : ty),*) => {
$(
impl Add<$t> for StandardForm {
type Output = Self;
fn add(self, other: $t) -> Self {
let rhs : Self = other.into();
self + rhs
}
}
impl AddAssign<$t> for StandardForm {
fn add_assign(&mut self, other: $t) {
let rhs : Self = other.into();
*self += rhs;
}
}
)*
};
(sub => $($t : ty),*) => {
$(
impl Sub<$t> for StandardForm {
type Output = Self;
fn sub(self, other: $t) -> Self {
let rhs : Self = other.into();
self - rhs
}
}
impl SubAssign<$t> for StandardForm {
fn sub_assign(&mut self, other: $t) {
let rhs : Self = other.into();
*self -= rhs;
}
}
)*
};
(mul => $($t : ty),*) => {
$(
impl Mul<$t> for StandardForm {
type Output = Self;
fn mul(self, other: $t) -> Self {
let rhs : Self = other.into();
self * rhs
}
}
impl MulAssign<$t> for StandardForm {
fn mul_assign(&mut self, other: $t) {
let rhs : Self = other.into();
*self *= rhs;
}
}
)*
};
(div => $($t : ty),*) => {
$(
impl Div<$t> for StandardForm {
type Output = Self;
fn div(self, other: $t) -> Self {
let rhs : Self = other.into();
self / rhs
}
}
impl DivAssign<$t> for StandardForm {
fn div_assign(&mut self, other: $t) {
let rhs : Self = other.into();
*self /= rhs;
}
}
)*
};
(operations => $($t:ty),*) => {
$(
primitives!(add => $t);
primitives!(sub => $t);
primitives!(mul => $t);
primitives!(div => $t);
)*
}
}
primitives!(operations => i8, i16, i32, i64, u8, u16, u32, u64,f32,f64);
primitives!(form => u8,u16,u32,u64,i8,i16,i32,i64,f32,f64);
primitives!(eq => u8,u16,u32,u64,i8,i16,i32,i64,f32,f64);
primitives!(ord => u8,u16,u32,u64,i8,i16,i32,i64,f32,f64);
#[cfg(test)]
mod tests {
use super::*;
use std::cmp::Ordering;
#[test]
fn assignment_issue() {
let sf1 = StandardForm::new(1.0,5);
assert_eq!(*sf1.mantissa(),1.0);
assert_eq!(*sf1.exponent(),5);
}
#[test]
fn from_u8_standardform(){
let n = 2u8;
let r : StandardForm = n.into();
assert_eq!(r,StandardForm { mantissa : 2.0,exponent : 0 });
}
#[test]
fn test_normalize_with_valid_range() {
let mut sf = StandardForm::new(2.5, 3);
sf.adjust();
assert_eq!(sf.mantissa, 2.5);
assert_eq!(sf.exponent, 3);
}
#[test]
fn test_normalize_with_invalid_range() {
let mut sf = StandardForm::new(20.0, 3);
sf.adjust();
assert_eq!(sf.mantissa, 2.0);
assert_eq!(sf.exponent, 4);
}
#[test]
fn test_normalize_with_small_mantissa() {
let mut sf = StandardForm::new(-0.25, 2);
sf.adjust();
assert_eq!(sf.mantissa, -2.5);
assert_eq!(sf.exponent, 1);
}
#[test]
fn test_normalize_with_large_negative_mantissa() {
let mut sf = StandardForm::new(-750.0, 4);
sf.adjust();
assert_eq!(sf.mantissa, -7.5);
assert_eq!(sf.exponent, 6);
}
#[test]
fn addition() {
let a = StandardForm::new(1.2, 3);
let b = StandardForm::new(3.4, 2);
let result = a + b;
assert_eq!(result, StandardForm::new(1.54,3) );
}
#[test]
fn addition_u8() {
let a = StandardForm::new(1.0, 1);
let b = 2u8;
let result = a + b;
assert_eq!(result, StandardForm::new(1.2,1));
}
#[test]
fn test_subtraction() {
let a = StandardForm::new(4.6, 2);
let b = StandardForm::new(3.4, 2);
let result = a - b;
assert_eq!(result, StandardForm::new(1.2,2));
}
#[test]
fn multiplication() {
let a = StandardForm::new(1.2, 3);
let b = StandardForm::new(3.0, 2);
let result = a * b;
assert_eq!(result.mantissa, 3.6);
assert_eq!(result.exponent, 5);
}
#[test]
fn multiplication_u8() {
let a = StandardForm::new(1.0, 1);
let b = 2u8;
let result = a * b;
assert_eq!(result.mantissa, 2.0);
assert_eq!(result.exponent, 1);
}
#[test]
fn division() {
let a = StandardForm::new(4.0, 2);
let b = StandardForm::new(2.0, 1);
let result = a / b;
assert_eq!(result.mantissa, 2.0);
assert_eq!(result.exponent, 1);
}
#[test]
fn division_u8() {
let a = StandardForm::new(2.0, 1);
let b = 2u8;
let result = a / b;
assert_eq!(result.mantissa, 1.0);
assert_eq!(result.exponent, 1);
}
#[test]
fn add_assign() {
let mut a = StandardForm::new(1.0, 1);
let b = StandardForm::new(2.0, 1);
a += b;
assert_eq!(a.mantissa, 3.0);
assert_eq!(a.exponent, 1);
}
#[test]
fn add_assign_u8() {
let mut a = StandardForm::new(1.0, 1);
let b = 2u8;
a += b;
assert_eq!(a.mantissa, 1.2);
assert_eq!(a.exponent, 1);
}
#[test]
fn test_partial_cmp_equal() {
let sf1 = StandardForm::new(1.23, 3);
let sf2 = StandardForm::new(1.23, 3);
assert_eq!(sf1.partial_cmp(&sf2), Some(Ordering::Equal));
}
#[test]
fn test_partial_cmp_greater() {
let sf1 = StandardForm::new(3.0, 2);
let sf2 = StandardForm::new(2.5, 2);
assert_eq!(sf1.partial_cmp(&sf2), Some(Ordering::Greater));
}
#[test]
fn test_partial_cmp_less() {
let sf1 = StandardForm::new(2.5, 2);
let sf2 = StandardForm::new(3.0, 2);
assert_eq!(sf1.partial_cmp(&sf2), Some(Ordering::Less));
}
#[test]
fn test_partial_cmp_different_exponents() {
let sf1 = StandardForm::new(1.5, 2);
let sf2 = StandardForm::new(1.5, 3);
assert_eq!(sf1.partial_cmp(&sf2), Some(Ordering::Less));
}
#[test]
fn test_partial_cmp_zero() {
let sf1 = StandardForm::new(0.0, 0);
let sf2 = StandardForm::new(0.0, 0);
assert_eq!(sf1.partial_cmp(&sf2), Some(Ordering::Equal));
}
#[test]
fn test_partial_cmp_mixed_sign() {
let sf1 = StandardForm::new(-1.0, 2);
let sf2 = StandardForm::new(1.0, 2);
assert_eq!(sf1.partial_cmp(&sf2), Some(Ordering::Less));
}
}