Skip to main content

unsigned_float/
uf32.rs

1use core::cmp::Ordering;
2use core::fmt;
3use core::ops::{Add, Div, Mul, Sub};
4
5use crate::{ConversionError, dispatch};
6
7macro_rules! impl_float_format {
8    ($ty:ty, $to_float:ident) => {
9        impl fmt::Display for $ty {
10            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
11                fmt::Display::fmt(&self.$to_float(), f)
12            }
13        }
14
15        impl fmt::LowerExp for $ty {
16            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
17                fmt::LowerExp::fmt(&self.$to_float(), f)
18            }
19        }
20
21        impl fmt::UpperExp for $ty {
22            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23                fmt::UpperExp::fmt(&self.$to_float(), f)
24            }
25        }
26    };
27}
28
29#[derive(Clone, Copy, Default, Hash, PartialEq, Eq)]
30#[repr(transparent)]
31/// A 32-bit unsigned float with 8 exponent bits and 24 mantissa bits.
32pub struct Uf32E8M24(u32);
33
34/// Default 32-bit unsigned float layout.
35pub type Uf32 = Uf32E8M24;
36
37impl Uf32E8M24 {
38    pub const EXPONENT_BITS: u32 = 8;
39    pub const MANTISSA_BITS: u32 = 24;
40    pub const EXPONENT_BIAS: i32 = 127;
41    pub const EXPONENT_MASK: u32 = 0xff00_0000;
42    pub const MANTISSA_MASK: u32 = 0x00ff_ffff;
43
44    pub const ZERO: Self = Self(0);
45    pub const ONE: Self = Self(0x7f00_0000);
46    pub const INFINITY: Self = Self(0xff00_0000);
47    pub const NAN: Self = Self(0xff80_0000);
48    pub const MAX: Self = Self(0xfeff_ffff);
49    pub const MIN_POSITIVE: Self = Self(0x0000_0001);
50    pub const MIN_NORMAL: Self = Self(0x0100_0000);
51
52    pub const fn from_bits(bits: u32) -> Self {
53        Self(bits)
54    }
55
56    pub const fn to_bits(self) -> u32 {
57        self.0
58    }
59
60    pub fn from_f64(value: f64) -> Self {
61        Self(dispatch::f64_to_uf32(value))
62    }
63
64    pub fn to_f64(self) -> f64 {
65        dispatch::uf32_to_f64(self.0)
66    }
67
68    pub fn try_from_f64(value: f64) -> Result<Self, ConversionError> {
69        crate::convert::check_finite_non_negative(value)?;
70
71        let encoded = Self::from_f64(value);
72        crate::convert::check_encoded(value, encoded.is_zero(), encoded.is_infinite())?;
73
74        Ok(encoded)
75    }
76
77    pub fn from_f32(value: f32) -> Self {
78        Self::from_f64(value as f64)
79    }
80
81    pub fn to_f32(self) -> f32 {
82        self.to_f64() as f32
83    }
84
85    #[cfg(feature = "f16")]
86    pub fn from_f16(value: f16) -> Self {
87        Self::from_f64(value as f64)
88    }
89
90    #[cfg(feature = "f16")]
91    pub fn to_f16(self) -> f16 {
92        self.to_f64() as f16
93    }
94
95    pub const fn exponent(self) -> u32 {
96        (self.0 & Self::EXPONENT_MASK) >> Self::MANTISSA_BITS
97    }
98
99    pub const fn mantissa(self) -> u32 {
100        self.0 & Self::MANTISSA_MASK
101    }
102
103    pub const fn is_zero(self) -> bool {
104        self.0 == 0
105    }
106
107    pub const fn is_nan(self) -> bool {
108        self.exponent() == 0xff && self.mantissa() != 0
109    }
110
111    pub const fn is_infinite(self) -> bool {
112        self.0 == Self::INFINITY.0
113    }
114
115    pub const fn is_finite(self) -> bool {
116        self.exponent() != 0xff
117    }
118
119    pub const fn is_subnormal(self) -> bool {
120        self.exponent() == 0 && self.mantissa() != 0
121    }
122}
123
124impl From<f32> for Uf32E8M24 {
125    fn from(value: f32) -> Self {
126        Self::from_f32(value)
127    }
128}
129
130#[cfg(feature = "f16")]
131impl From<f16> for Uf32E8M24 {
132    fn from(value: f16) -> Self {
133        Self::from_f16(value)
134    }
135}
136
137#[cfg(feature = "f16")]
138impl From<Uf32E8M24> for f16 {
139    fn from(value: Uf32E8M24) -> Self {
140        value.to_f16()
141    }
142}
143
144impl From<Uf32E8M24> for f32 {
145    fn from(value: Uf32E8M24) -> Self {
146        value.to_f32()
147    }
148}
149
150impl From<Uf32E8M24> for f64 {
151    fn from(value: Uf32E8M24) -> Self {
152        value.to_f64()
153    }
154}
155
156impl Ord for Uf32E8M24 {
157    fn cmp(&self, other: &Self) -> Ordering {
158        self.0.cmp(&other.0)
159    }
160}
161
162impl PartialOrd for Uf32E8M24 {
163    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
164        Some(self.cmp(other))
165    }
166}
167
168impl Add for Uf32E8M24 {
169    type Output = Self;
170
171    fn add(self, rhs: Self) -> Self::Output {
172        Self(dispatch::add_uf32(self.0, rhs.0))
173    }
174}
175
176impl Sub for Uf32E8M24 {
177    type Output = Self;
178
179    fn sub(self, rhs: Self) -> Self::Output {
180        Self(dispatch::sub_uf32(self.0, rhs.0))
181    }
182}
183
184impl Mul for Uf32E8M24 {
185    type Output = Self;
186
187    fn mul(self, rhs: Self) -> Self::Output {
188        Self(dispatch::mul_uf32(self.0, rhs.0))
189    }
190}
191
192impl Div for Uf32E8M24 {
193    type Output = Self;
194
195    fn div(self, rhs: Self) -> Self::Output {
196        Self(dispatch::div_uf32(self.0, rhs.0))
197    }
198}
199
200impl fmt::Debug for Uf32E8M24 {
201    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
202        f.debug_tuple("Uf32E8M24").field(&self.to_f64()).finish()
203    }
204}
205
206impl_float_format!(Uf32E8M24, to_f64);