1use crate::EnumSetType;
2use cubecl_common::{e4m3, e5m2, ue8m0};
3use pliron::{context::Context, r#type::TypeHandle};
4
5use crate::types::scalar::{Float8E4M3Type, Float8E5M2Type, Float8E8M0Type};
6
7const FP8_MAGNITUDE_MASK: u32 = 0x7F;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EnumSetType)]
18pub enum Fp8Format {
19 E4M3,
20 E5M2,
21 UE8M0,
22}
23
24impl Fp8Format {
25 pub const fn exponent_bits(self) -> u32 {
26 match self {
27 Fp8Format::UE8M0 => 8,
29 _ => 7 - self.mantissa_bits(),
30 }
31 }
32
33 pub const fn mantissa_bits(self) -> u32 {
34 match self {
35 Fp8Format::E4M3 => e4m3::MANTISSA_DIGITS - 1,
36 Fp8Format::E5M2 => e5m2::MANTISSA_DIGITS - 1,
37 Fp8Format::UE8M0 => 0,
38 }
39 }
40
41 pub const fn bias(self) -> u32 {
42 let min_exp = match self {
43 Fp8Format::E4M3 => e4m3::MIN_EXP,
44 Fp8Format::E5M2 => e5m2::MIN_EXP,
45 Fp8Format::UE8M0 => return 127,
48 };
49 (2 - min_exp) as u32
50 }
51
52 pub const fn max_value(self) -> f32 {
53 match self {
54 Fp8Format::E4M3 => e4m3::MAX.to_f32(),
55 Fp8Format::E5M2 => e5m2::MAX.to_f32(),
56 Fp8Format::UE8M0 => f32::from_bits(0x7F00_0000),
58 }
59 }
60
61 pub const fn max_code(self) -> u32 {
62 match self {
63 Fp8Format::E4M3 => e4m3::MAX.to_bits() as u32,
64 Fp8Format::E5M2 => e5m2::MAX.to_bits() as u32,
65 Fp8Format::UE8M0 => ue8m0::MAX.to_bits() as u32,
66 }
67 }
68
69 pub const fn nan_code(self) -> u32 {
74 let nan = match self {
75 Fp8Format::E4M3 => e4m3::from_f32(f32::NAN).to_bits(),
76 Fp8Format::E5M2 => e5m2::from_f32(f32::NAN).to_bits(),
77 Fp8Format::UE8M0 => return 0xFF,
79 };
80 nan as u32 & FP8_MAGNITUDE_MASK
81 }
82
83 pub const fn has_infinity(self) -> bool {
84 match self {
85 Fp8Format::UE8M0 => false,
87 _ => self.decode(self.max_code() + 1).is_infinite(),
88 }
89 }
90
91 pub const fn min_normal(self) -> f32 {
92 match self {
93 Fp8Format::E4M3 => e4m3::MIN_POSITIVE.to_f32(),
94 Fp8Format::E5M2 => e5m2::MIN_POSITIVE.to_f32(),
95 Fp8Format::UE8M0 => f32::from_bits(0x0040_0000),
98 }
99 }
100
101 pub const fn subnormal_step(self) -> f32 {
102 match self {
103 Fp8Format::UE8M0 => 0.0,
105 _ => self.decode(1),
106 }
107 }
108
109 const fn decode(self, code: u32) -> f32 {
110 match self {
111 Fp8Format::E4M3 => e4m3::from_bits(code as u8).to_f32(),
112 Fp8Format::E5M2 => e5m2::from_bits(code as u8).to_f32(),
113 Fp8Format::UE8M0 => f32::from_bits(code << 23),
115 }
116 }
117
118 pub fn of_type(ctx: &Context, ty: TypeHandle) -> Option<Self> {
119 let ty = ty.deref(ctx);
120 if ty.is::<Float8E4M3Type>() {
121 Some(Fp8Format::E4M3)
122 } else if ty.is::<Float8E5M2Type>() {
123 Some(Fp8Format::E5M2)
124 } else if ty.is::<Float8E8M0Type>() {
125 Some(Fp8Format::UE8M0)
126 } else {
127 None
128 }
129 }
130}