From 9e23ce05a9f3eb57bb372128efd03e8a432a996b Mon Sep 17 00:00:00 2001 From: numzero Date: Sun, 2 Nov 2025 14:49:38 +0300 Subject: [PATCH] move conversions into a file --- src/conv.rs | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 121 +------------------------------------------------ 2 files changed, 127 insertions(+), 120 deletions(-) create mode 100644 src/conv.rs diff --git a/src/conv.rs b/src/conv.rs new file mode 100644 index 0000000..8472df9 --- /dev/null +++ b/src/conv.rs @@ -0,0 +1,126 @@ +use super::*; + +impl From for F8 { + fn from(v: u8) -> Self { + if v == 0 { + return Self(0); + } + let e = v.ilog2() as u8; + let off = e as i8 - M_BITS as i8; + let m = if off >= 0 { v >> off } else { v << -off }; + if e > E_MAX { + return Self(0xff); + } + Self::merge(m & M_STORAGE_MAX, e + E_BIAS) + } +} + +impl From for u8 { + fn from(value: F8) -> Self { + if value.0 == 0 { + return 0; + } + let (m, e) = value.split_unbias(); + match e { + 0.. => m << e, + -7..0 => m >> -e, + ..-7 => 0, + } + } +} + +impl From for f32 { + fn from(value: F8) -> Self { + if value.0 == 0 { + return 0.0; + } + let (m, e) = value.split_unbias(); + (m as f32) * (e as f32).exp2() + } +} + +impl F8 { + /// Split self into the mantissa and exponent, as stored. + pub(crate) fn split(self) -> (u8, u8) { + (self.0 & M_MASK, self.0 >> M_BITS) + } + + /// Split self into integers (m, e) such that `self == m * 2.pow(e)`. + pub(crate) fn split_unbias(self) -> (u8, i8) { + let (m, e) = self.split(); + (m | M_BIAS, e as i8 - (E_BIAS + M_BITS) as i8) + } + + pub(crate) fn merge(m: u8, e: u8) -> Self { + assert!(m <= M_STORAGE_MAX); + assert!(e <= E_STORAGE_MAX); + Self((e << M_BITS) | m) + } + + pub(crate) fn merge_unbias(in_m: u32, in_e: i32) -> Self { + if in_m == 0 { + return Self(0); + } + let base_e = in_m.ilog2() as u8; + let off = base_e as i8 - M_BITS as i8; + let m = if off >= 0 { in_m >> off } else { in_m << -off }; + let e = (base_e as i32) + in_e + (E_BIAS as i32); + if e < 0 { + return Self(0); + } + if e > E_STORAGE_MAX as i32 { + return Self(0xff); + } + Self::merge(m as u8 & M_STORAGE_MAX, e as u8) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_int_conv() { + assert_eq!(u8::from(F8(0)), 0); + assert_eq!(u8::from(F8::merge(0, 1)), 0); + assert_eq!(u8::from(F8::merge(0, E_BIAS - 1)), 0); + assert_eq!(u8::from(F8::merge(0, E_BIAS)), 1); + assert_eq!(u8::from(F8::merge(0, E_BIAS + 1)), 2); + assert_eq!(u8::from(F8::merge(0, E_STORAGE_MAX)), 1 << E_MAX); + for k in 0..=EXACT_INT_MAX { + assert_eq!(u8::from(F8::from(k)), k); + } + } + + #[test] + fn test_float_conv() { + assert_eq!(f32::from(F8(0)), 0.0); + assert_eq!(f32::from(F8::merge(0, E_BIAS)), 1.0); + assert_eq!(f32::from(F8::merge(0, E_BIAS - 1)), 0.5); + assert_eq!(f32::from(F8::merge(0, E_BIAS + 1)), 2.0); + assert_eq!(f32::from(F8::merge(1 << (M_BITS - 1), E_BIAS)), 1.5); + assert_eq!(f32::from(F8::merge(1 << (M_BITS - 1), E_BIAS - 1)), 0.75); + assert_eq!( + f32::from(F8::merge(0, E_STORAGE_MAX)), + (E_MAX as f32).exp2() + ); + for k in 0..=EXACT_INT_MAX { + assert_eq!(f32::from(F8::from(k)), k as f32); + } + } + + #[test] + fn test_merge() { + assert_eq!(f32::from(F8::merge_unbias(0, 0)), 0.0); + assert_eq!(f32::from(F8::merge_unbias(1, 0)), 1.0); + assert_eq!(f32::from(F8::merge_unbias(1, 1)), 2.0); + assert_eq!(f32::from(F8::merge_unbias(1, -1)), 0.5); + assert_eq!(f32::from(F8::merge_unbias(3, 0)), 3.0); + assert_eq!(f32::from(F8::merge_unbias(3, 1)), 6.0); + assert_eq!(f32::from(F8::merge_unbias(3, -1)), 1.5); + assert_eq!( + f32::from(F8::merge_unbias(EXACT_INT_MAX.into(), 0)), + EXACT_INT_MAX as f32 + ); + } +} diff --git a/src/lib.rs b/src/lib.rs index bdd4355..fb3d5b6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ +mod conv; mod ops; pub const M_BITS: u8 = 5; @@ -23,42 +24,6 @@ const E_MASK: u8 = E_STORAGE_MAX << M_BITS; #[repr(transparent)] pub struct F8(u8); -impl F8 { - /// Split self into the mantissa and exponent, as stored. - fn split(self) -> (u8, u8) { - (self.0 & M_MASK, self.0 >> M_BITS) - } - - /// Split self into integers (m, e) such that `self == m * 2.pow(e)`. - fn split_unbias(self) -> (u8, i8) { - let (m, e) = self.split(); - (m | M_BIAS, e as i8 - (E_BIAS + M_BITS) as i8) - } - - fn merge(m: u8, e: u8) -> Self { - assert!(m <= M_STORAGE_MAX); - assert!(e <= E_STORAGE_MAX); - Self((e << M_BITS) | m) - } - - fn merge_unbias(in_m: u32, in_e: i32) -> Self { - if in_m == 0 { - return Self(0); - } - let base_e = in_m.ilog2() as u8; - let off = base_e as i8 - M_BITS as i8; - let m = if off >= 0 { in_m >> off } else { in_m << -off }; - let e = (base_e as i32) + in_e + (E_BIAS as i32); - if e < 0 { - return Self(0); - } - if e > E_STORAGE_MAX as i32 { - return Self(0xff); - } - Self::merge(m as u8 & M_STORAGE_MAX, e as u8) - } -} - impl std::fmt::Binary for F8 { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { if f.alternate() { @@ -80,94 +45,10 @@ impl std::fmt::Debug for F8 { } } -impl From for F8 { - fn from(v: u8) -> Self { - if v == 0 { - return Self(0); - } - let e = v.ilog2() as u8; - let off = e as i8 - M_BITS as i8; - let m = if off >= 0 { v >> off } else { v << -off }; - if e > E_MAX { - return Self(0xff); - } - Self::merge(m & M_STORAGE_MAX, e + E_BIAS) - } -} - -impl From for u8 { - fn from(value: F8) -> Self { - if value.0 == 0 { - return 0; - } - let (m, e) = value.split_unbias(); - match e { - 0.. => m << e, - -7..0 => m >> -e, - ..-7 => 0, - } - } -} - -impl From for f32 { - fn from(value: F8) -> Self { - if value.0 == 0 { - return 0.0; - } - let (m, e) = value.split_unbias(); - (m as f32) * (e as f32).exp2() - } -} - #[cfg(test)] mod tests { use super::*; - #[test] - fn test_int_conv() { - assert_eq!(u8::from(F8(0)), 0); - assert_eq!(u8::from(F8::merge(0, 1)), 0); - assert_eq!(u8::from(F8::merge(0, E_BIAS - 1)), 0); - assert_eq!(u8::from(F8::merge(0, E_BIAS)), 1); - assert_eq!(u8::from(F8::merge(0, E_BIAS + 1)), 2); - assert_eq!(u8::from(F8::merge(0, E_STORAGE_MAX)), 1 << E_MAX); - for k in 0..=EXACT_INT_MAX { - assert_eq!(u8::from(F8::from(k)), k); - } - } - - #[test] - fn test_float_conv() { - assert_eq!(f32::from(F8(0)), 0.0); - assert_eq!(f32::from(F8::merge(0, E_BIAS)), 1.0); - assert_eq!(f32::from(F8::merge(0, E_BIAS - 1)), 0.5); - assert_eq!(f32::from(F8::merge(0, E_BIAS + 1)), 2.0); - assert_eq!(f32::from(F8::merge(1 << (M_BITS - 1), E_BIAS)), 1.5); - assert_eq!(f32::from(F8::merge(1 << (M_BITS - 1), E_BIAS - 1)), 0.75); - assert_eq!( - f32::from(F8::merge(0, E_STORAGE_MAX)), - (E_MAX as f32).exp2() - ); - for k in 0..=EXACT_INT_MAX { - assert_eq!(f32::from(F8::from(k)), k as f32); - } - } - - #[test] - fn test_merge() { - assert_eq!(f32::from(F8::merge_unbias(0, 0)), 0.0); - assert_eq!(f32::from(F8::merge_unbias(1, 0)), 1.0); - assert_eq!(f32::from(F8::merge_unbias(1, 1)), 2.0); - assert_eq!(f32::from(F8::merge_unbias(1, -1)), 0.5); - assert_eq!(f32::from(F8::merge_unbias(3, 0)), 3.0); - assert_eq!(f32::from(F8::merge_unbias(3, 1)), 6.0); - assert_eq!(f32::from(F8::merge_unbias(3, -1)), 1.5); - assert_eq!( - f32::from(F8::merge_unbias(EXACT_INT_MAX.into(), 0)), - EXACT_INT_MAX as f32 - ); - } - #[test] fn test_display() { fn fmt_split(m: u8, e: u8) -> String {