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)]
9pub struct Uf8E4M4(u8);
11
12pub 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)]
186pub 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}