use crate::EnumSetType;
use cubecl_common::{e4m3, e5m2, ue8m0};
use pliron::{context::Context, r#type::TypeHandle};
use crate::types::scalar::{Float8E4M3Type, Float8E5M2Type, Float8E8M0Type};
const FP8_MAGNITUDE_MASK: u32 = 0x7F;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EnumSetType)]
pub enum Fp8Format {
E4M3,
E5M2,
UE8M0,
}
impl Fp8Format {
pub const fn exponent_bits(self) -> u32 {
match self {
Fp8Format::UE8M0 => 8,
_ => 7 - self.mantissa_bits(),
}
}
pub const fn mantissa_bits(self) -> u32 {
match self {
Fp8Format::E4M3 => e4m3::MANTISSA_DIGITS - 1,
Fp8Format::E5M2 => e5m2::MANTISSA_DIGITS - 1,
Fp8Format::UE8M0 => 0,
}
}
pub const fn bias(self) -> u32 {
let min_exp = match self {
Fp8Format::E4M3 => e4m3::MIN_EXP,
Fp8Format::E5M2 => e5m2::MIN_EXP,
Fp8Format::UE8M0 => return 127,
};
(2 - min_exp) as u32
}
pub const fn max_value(self) -> f32 {
match self {
Fp8Format::E4M3 => e4m3::MAX.to_f32(),
Fp8Format::E5M2 => e5m2::MAX.to_f32(),
Fp8Format::UE8M0 => f32::from_bits(0x7F00_0000),
}
}
pub const fn max_code(self) -> u32 {
match self {
Fp8Format::E4M3 => e4m3::MAX.to_bits() as u32,
Fp8Format::E5M2 => e5m2::MAX.to_bits() as u32,
Fp8Format::UE8M0 => ue8m0::MAX.to_bits() as u32,
}
}
pub const fn nan_code(self) -> u32 {
let nan = match self {
Fp8Format::E4M3 => e4m3::from_f32(f32::NAN).to_bits(),
Fp8Format::E5M2 => e5m2::from_f32(f32::NAN).to_bits(),
Fp8Format::UE8M0 => return 0xFF,
};
nan as u32 & FP8_MAGNITUDE_MASK
}
pub const fn has_infinity(self) -> bool {
match self {
Fp8Format::UE8M0 => false,
_ => self.decode(self.max_code() + 1).is_infinite(),
}
}
pub const fn min_normal(self) -> f32 {
match self {
Fp8Format::E4M3 => e4m3::MIN_POSITIVE.to_f32(),
Fp8Format::E5M2 => e5m2::MIN_POSITIVE.to_f32(),
Fp8Format::UE8M0 => f32::from_bits(0x0040_0000),
}
}
pub const fn subnormal_step(self) -> f32 {
match self {
Fp8Format::UE8M0 => 0.0,
_ => self.decode(1),
}
}
const fn decode(self, code: u32) -> f32 {
match self {
Fp8Format::E4M3 => e4m3::from_bits(code as u8).to_f32(),
Fp8Format::E5M2 => e5m2::from_bits(code as u8).to_f32(),
Fp8Format::UE8M0 => f32::from_bits(code << 23),
}
}
pub fn of_type(ctx: &Context, ty: TypeHandle) -> Option<Self> {
let ty = ty.deref(ctx);
if ty.is::<Float8E4M3Type>() {
Some(Fp8Format::E4M3)
} else if ty.is::<Float8E5M2Type>() {
Some(Fp8Format::E5M2)
} else if ty.is::<Float8E8M0Type>() {
Some(Fp8Format::UE8M0)
} else {
None
}
}
}