Skip to main content

unsigned_float/
uf8.rs

1use core::cmp::Ordering;
2use core::fmt;
3use core::ops::{Add, Div, Mul, Sub};
4
5use crate::{ConversionError, dispatch};
6
7#[derive(Clone, Copy, Default, Hash, PartialEq, Eq)]
8#[repr(transparent)]
9/// An 8-bit unsigned float with 4 exponent bits and 4 mantissa bits.
10pub struct Uf8E4M4(u8);
11
12/// Default 8-bit unsigned float layout.
13pub type Uf8 = Uf8E4M4;
14
15impl Uf8E4M4 {
16    pub const EXPONENT_BITS: u32 = 4;
17    pub const MANTISSA_BITS: u32 = 4;
18    pub const EXPONENT_BIAS: i32 = 7;
19    pub const EXPONENT_MASK: u8 = 0xf0;
20    pub const MANTISSA_MASK: u8 = 0x0f;
21
22    pub const ZERO: Self = Self(0);
23    pub const ONE: Self = Self(0x70);
24    pub const INFINITY: Self = Self(0xf0);
25    pub const NAN: Self = Self(0xf8);
26    pub const MAX: Self = Self(0xef);
27    pub const MIN_POSITIVE: Self = Self(0x01);
28    pub const MIN_NORMAL: Self = Self(0x10);
29
30    pub const fn from_bits(bits: u8) -> Self {
31        Self(bits)
32    }
33
34    pub const fn to_bits(self) -> u8 {
35        self.0
36    }
37
38    pub fn from_f32(value: f32) -> Self {
39        Self(dispatch::f32_to_uf8(value))
40    }
41
42    pub fn to_f32(self) -> f32 {
43        dispatch::uf8_to_f32(self.0)
44    }
45
46    pub fn from_f64(value: f64) -> Self {
47        Self(dispatch::f32_to_uf8(value as f32))
48    }
49
50    pub fn to_f64(self) -> f64 {
51        self.to_f32() as f64
52    }
53
54    pub fn try_from_f64(value: f64) -> Result<Self, ConversionError> {
55        crate::convert::check_finite_non_negative(value)?;
56
57        let encoded = Self::from_f64(value);
58        crate::convert::check_encoded(value, encoded.is_zero(), encoded.is_infinite())?;
59
60        Ok(encoded)
61    }
62
63    #[cfg(feature = "f16")]
64    pub fn from_f16(value: f16) -> Self {
65        Self::from_f32(value as f32)
66    }
67
68    #[cfg(feature = "f16")]
69    pub fn to_f16(self) -> f16 {
70        self.to_f32() as f16
71    }
72
73    pub const fn exponent(self) -> u8 {
74        (self.0 & Self::EXPONENT_MASK) >> Self::MANTISSA_BITS
75    }
76
77    pub const fn mantissa(self) -> u8 {
78        self.0 & Self::MANTISSA_MASK
79    }
80
81    pub const fn is_zero(self) -> bool {
82        self.0 == 0
83    }
84
85    pub const fn is_nan(self) -> bool {
86        self.exponent() == 0x0f && self.mantissa() != 0
87    }
88
89    pub const fn is_infinite(self) -> bool {
90        self.0 == Self::INFINITY.0
91    }
92
93    pub const fn is_finite(self) -> bool {
94        self.exponent() != 0x0f
95    }
96
97    pub const fn is_subnormal(self) -> bool {
98        self.exponent() == 0 && self.mantissa() != 0
99    }
100}
101
102impl From<f32> for Uf8E4M4 {
103    fn from(value: f32) -> Self {
104        Self::from_f32(value)
105    }
106}
107
108#[cfg(feature = "f16")]
109impl From<f16> for Uf8E4M4 {
110    fn from(value: f16) -> Self {
111        Self::from_f16(value)
112    }
113}
114
115impl From<Uf8E4M4> for f32 {
116    fn from(value: Uf8E4M4) -> Self {
117        value.to_f32()
118    }
119}
120
121#[cfg(feature = "f16")]
122impl From<Uf8E4M4> for f16 {
123    fn from(value: Uf8E4M4) -> Self {
124        value.to_f16()
125    }
126}
127
128impl From<Uf8E4M4> for f64 {
129    fn from(value: Uf8E4M4) -> Self {
130        value.to_f64()
131    }
132}
133
134impl Ord for Uf8E4M4 {
135    fn cmp(&self, other: &Self) -> Ordering {
136        self.0.cmp(&other.0)
137    }
138}
139
140impl PartialOrd for Uf8E4M4 {
141    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
142        Some(self.cmp(other))
143    }
144}
145
146impl Add for Uf8E4M4 {
147    type Output = Self;
148
149    fn add(self, rhs: Self) -> Self::Output {
150        Self(dispatch::add_uf8(self.0, rhs.0))
151    }
152}
153
154impl Sub for Uf8E4M4 {
155    type Output = Self;
156
157    fn sub(self, rhs: Self) -> Self::Output {
158        Self(dispatch::sub_uf8(self.0, rhs.0))
159    }
160}
161
162impl Mul for Uf8E4M4 {
163    type Output = Self;
164
165    fn mul(self, rhs: Self) -> Self::Output {
166        Self(dispatch::mul_uf8(self.0, rhs.0))
167    }
168}
169
170impl Div for Uf8E4M4 {
171    type Output = Self;
172
173    fn div(self, rhs: Self) -> Self::Output {
174        Self(dispatch::div_uf8(self.0, rhs.0))
175    }
176}
177
178impl fmt::Debug for Uf8E4M4 {
179    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
180        f.debug_tuple("Uf8E4M4").field(&self.to_f32()).finish()
181    }
182}
183
184#[derive(Clone, Copy, Default, Hash, PartialEq, Eq)]
185#[repr(transparent)]
186/// An 8-bit unsigned float with 5 exponent bits and 3 mantissa bits.
187pub struct Uf8E5M3(u8);
188
189impl Uf8E5M3 {
190    pub const EXPONENT_BITS: u32 = 5;
191    pub const MANTISSA_BITS: u32 = 3;
192    pub const EXPONENT_BIAS: i32 = 15;
193    pub const EXPONENT_MASK: u8 = 0xf8;
194    pub const MANTISSA_MASK: u8 = 0x07;
195
196    pub const ZERO: Self = Self(0);
197    pub const ONE: Self = Self(0x78);
198    pub const INFINITY: Self = Self(0xf8);
199    pub const NAN: Self = Self(0xfc);
200    pub const MAX: Self = Self(0xf7);
201    pub const MIN_POSITIVE: Self = Self(0x01);
202    pub const MIN_NORMAL: Self = Self(0x08);
203
204    pub const fn from_bits(bits: u8) -> Self {
205        Self(bits)
206    }
207
208    pub const fn to_bits(self) -> u8 {
209        self.0
210    }
211
212    pub fn from_f32(value: f32) -> Self {
213        Self(dispatch::f32_to_uf8_e5m3(value))
214    }
215
216    pub fn to_f32(self) -> f32 {
217        dispatch::uf8_e5m3_to_f32(self.0)
218    }
219
220    pub fn from_f64(value: f64) -> Self {
221        Self::from_f32(value as f32)
222    }
223
224    pub fn to_f64(self) -> f64 {
225        self.to_f32() as f64
226    }
227
228    pub fn try_from_f64(value: f64) -> Result<Self, ConversionError> {
229        crate::convert::check_finite_non_negative(value)?;
230
231        let encoded = Self::from_f64(value);
232        crate::convert::check_encoded(value, encoded.is_zero(), encoded.is_infinite())?;
233
234        Ok(encoded)
235    }
236
237    #[cfg(feature = "f16")]
238    pub fn from_f16(value: f16) -> Self {
239        Self::from_f32(value as f32)
240    }
241
242    #[cfg(feature = "f16")]
243    pub fn to_f16(self) -> f16 {
244        self.to_f32() as f16
245    }
246
247    pub const fn exponent(self) -> u8 {
248        (self.0 & Self::EXPONENT_MASK) >> Self::MANTISSA_BITS
249    }
250
251    pub const fn mantissa(self) -> u8 {
252        self.0 & Self::MANTISSA_MASK
253    }
254
255    pub const fn is_zero(self) -> bool {
256        self.0 == 0
257    }
258
259    pub const fn is_nan(self) -> bool {
260        self.exponent() == 0x1f && self.mantissa() != 0
261    }
262
263    pub const fn is_infinite(self) -> bool {
264        self.0 == Self::INFINITY.0
265    }
266
267    pub const fn is_finite(self) -> bool {
268        self.exponent() != 0x1f
269    }
270
271    pub const fn is_subnormal(self) -> bool {
272        self.exponent() == 0 && self.mantissa() != 0
273    }
274}
275
276impl From<f32> for Uf8E5M3 {
277    fn from(value: f32) -> Self {
278        Self::from_f32(value)
279    }
280}
281
282#[cfg(feature = "f16")]
283impl From<f16> for Uf8E5M3 {
284    fn from(value: f16) -> Self {
285        Self::from_f16(value)
286    }
287}
288
289impl From<Uf8E5M3> for f32 {
290    fn from(value: Uf8E5M3) -> Self {
291        value.to_f32()
292    }
293}
294
295#[cfg(feature = "f16")]
296impl From<Uf8E5M3> for f16 {
297    fn from(value: Uf8E5M3) -> Self {
298        value.to_f16()
299    }
300}
301
302impl From<Uf8E5M3> for f64 {
303    fn from(value: Uf8E5M3) -> Self {
304        value.to_f64()
305    }
306}
307
308impl Ord for Uf8E5M3 {
309    fn cmp(&self, other: &Self) -> Ordering {
310        self.0.cmp(&other.0)
311    }
312}
313
314impl PartialOrd for Uf8E5M3 {
315    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
316        Some(self.cmp(other))
317    }
318}
319
320impl Add for Uf8E5M3 {
321    type Output = Self;
322
323    fn add(self, rhs: Self) -> Self::Output {
324        Self(dispatch::add_uf8_e5m3(self.0, rhs.0))
325    }
326}
327
328impl Sub for Uf8E5M3 {
329    type Output = Self;
330
331    fn sub(self, rhs: Self) -> Self::Output {
332        Self(dispatch::sub_uf8_e5m3(self.0, rhs.0))
333    }
334}
335
336impl Mul for Uf8E5M3 {
337    type Output = Self;
338
339    fn mul(self, rhs: Self) -> Self::Output {
340        Self(dispatch::mul_uf8_e5m3(self.0, rhs.0))
341    }
342}
343
344impl Div for Uf8E5M3 {
345    type Output = Self;
346
347    fn div(self, rhs: Self) -> Self::Output {
348        Self(dispatch::div_uf8_e5m3(self.0, rhs.0))
349    }
350}
351
352impl fmt::Debug for Uf8E5M3 {
353    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
354        f.debug_tuple("Uf8E5M3").field(&self.to_f32()).finish()
355    }
356}