use crate::convert::{narrow, narrow_finite, widen, widen_finite};
#[derive(Copy, Clone, Default)]
#[repr(transparent)]
pub struct F16(pub u16);
#[derive(Copy, Clone, Default, PartialEq, PartialOrd, Debug)]
#[repr(transparent)]
pub struct F32(pub f32);
#[derive(Copy, Clone, Default, PartialEq, PartialOrd, Debug)]
#[repr(transparent)]
pub struct F64(pub f64);
#[derive(Copy, Clone, Default)]
#[repr(transparent)]
pub struct Bf16(pub u16);
#[derive(Copy, Clone, Default, PartialEq, PartialOrd, Debug)]
#[repr(transparent)]
pub struct Bf8(pub u8);
#[derive(Copy, Clone, Default, PartialEq, PartialOrd, Debug)]
#[repr(transparent)]
pub struct Bf4(pub u8);
#[derive(Copy, Clone, Default, PartialEq, PartialOrd, Debug)]
#[repr(transparent)]
pub struct F8(pub u8);
#[derive(Copy, Clone, Default, PartialEq, PartialOrd, Debug)]
#[repr(transparent)]
pub struct F4(pub u8);
impl F16 {
pub const ZERO: Self = Self(0x0000);
pub const ONE: Self = Self(0x3C00);
pub const NAN: Self = Self(0x7E00);
pub const INFINITY: Self = Self(0x7C00);
pub const NEG_INFINITY: Self = Self(0xFC00);
#[inline]
#[must_use]
pub const fn from_bits(bits: u16) -> Self {
Self(bits)
}
#[inline]
#[must_use]
pub const fn to_bits(self) -> u16 {
self.0
}
#[inline]
#[must_use]
pub fn to_f32(self) -> f32 {
f32::from_bits(widen::<5, 10>(self.0 as u32))
}
#[inline]
#[must_use]
pub fn from_f32(value: f32) -> Self {
Self(narrow::<5, 10>(value.to_bits()) as u16)
}
#[inline]
#[must_use]
pub fn from_f64(value: f64) -> Self {
Self::from_f32(value as f32)
}
#[inline]
#[must_use]
pub fn is_finite(self) -> bool {
(self.0 & 0x7C00) != 0x7C00
}
#[inline]
#[must_use]
pub fn is_nan(self) -> bool {
(self.0 & 0x7C00) == 0x7C00 && (self.0 & 0x03FF) != 0
}
#[inline]
pub fn widen_slice(src: &[Self], dst: &mut [f32]) {
crate::convert::widen_f16(crate::layout::cast_slice::<Self, u16>(src), dst);
}
#[inline]
pub fn narrow_slice(src: &[f32], dst: &mut [Self]) {
crate::convert::narrow_f16(src, crate::layout::cast_slice_mut::<Self, u16>(dst));
}
}
impl PartialEq for F16 {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.to_f32() == other.to_f32()
}
}
impl PartialOrd for F16 {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
self.to_f32().partial_cmp(&other.to_f32())
}
}
impl core::fmt::Debug for F16 {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "F16({})", self.to_f32())
}
}
impl Bf16 {
pub const ZERO: Self = Self(0x0000);
pub const ONE: Self = Self(0x3F80);
pub const NAN: Self = Self(0x7FC0);
pub const INFINITY: Self = Self(0x7F80);
pub const NEG_INFINITY: Self = Self(0xFF80);
#[inline]
#[must_use]
pub const fn from_bits(bits: u16) -> Self {
Self(bits)
}
#[inline]
#[must_use]
pub const fn to_bits(self) -> u16 {
self.0
}
#[inline]
#[must_use]
pub fn to_f32(self) -> f32 {
f32::from_bits(widen::<8, 7>(self.0 as u32))
}
#[inline]
#[must_use]
pub fn from_f32(value: f32) -> Self {
Self(narrow::<8, 7>(value.to_bits()) as u16)
}
#[inline]
#[must_use]
pub fn from_f64(value: f64) -> Self {
Self::from_f32(value as f32)
}
#[inline]
#[must_use]
pub fn is_finite(self) -> bool {
(self.0 & 0x7F80) != 0x7F80
}
#[inline]
#[must_use]
pub fn is_nan(self) -> bool {
(self.0 & 0x7F80) == 0x7F80 && (self.0 & 0x007F) != 0
}
#[inline]
pub fn widen_slice(src: &[Self], dst: &mut [f32]) {
crate::convert::widen_bf16(crate::layout::cast_slice::<Self, u16>(src), dst);
}
#[inline]
pub fn narrow_slice(src: &[f32], dst: &mut [Self]) {
crate::convert::narrow_bf16(src, crate::layout::cast_slice_mut::<Self, u16>(dst));
}
}
impl PartialEq for Bf16 {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.to_f32() == other.to_f32()
}
}
impl PartialOrd for Bf16 {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
self.to_f32().partial_cmp(&other.to_f32())
}
}
impl core::fmt::Debug for Bf16 {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "Bf16({})", self.to_f32())
}
}
impl Bf8 {
#[inline]
pub fn to_f32(self) -> f32 {
f32::from_bits(widen::<5, 2>(u32::from(self.0)))
}
#[inline]
pub fn from_f32(val: f32) -> Self {
Self(
u8::try_from(narrow::<5, 2>(val.to_bits()))
.expect("invariant: E5M2 encoding occupies exactly eight bits"),
)
}
}
impl Bf4 {
#[inline]
pub fn to_f32(self) -> f32 {
f32::from_bits(widen_finite::<2, 1>(u32::from(self.0)))
}
#[inline]
pub fn from_f32(val: f32) -> Self {
Self(
u8::try_from(narrow_finite::<2, 1>(val.to_bits()))
.expect("invariant: E2M1 encoding occupies four bits"),
)
}
#[inline]
pub fn pack_pair(low: Self, high: Self) -> u8 {
(low.0 & 0x0F) | ((high.0 & 0x0F) << 4)
}
#[inline]
pub fn unpack_pair(packed: u8) -> (Self, Self) {
(Self(packed & 0x0F), Self((packed >> 4) & 0x0F))
}
}
impl F8 {
#[inline]
pub fn to_f32(self) -> f32 {
f32::from_bits(widen_finite::<4, 3>(u32::from(self.0)))
}
#[inline]
pub fn from_f32(val: f32) -> Self {
Self(
u8::try_from(narrow_finite::<4, 3>(val.to_bits()))
.expect("invariant: E4M3 encoding occupies exactly eight bits"),
)
}
}
impl F4 {
#[inline]
pub fn to_f32(self) -> f32 {
f32::from_bits(widen_finite::<3, 0>(u32::from(self.0)))
}
#[inline]
pub fn from_f32(val: f32) -> Self {
Self(
u8::try_from(narrow_finite::<3, 0>(val.to_bits()))
.expect("invariant: E3M0 encoding occupies four bits"),
)
}
#[inline]
pub fn pack_pair(low: Self, high: Self) -> u8 {
(low.0 & 0x0F) | ((high.0 & 0x0F) << 4)
}
#[inline]
pub fn unpack_pair(packed: u8) -> (Self, Self) {
(Self(packed & 0x0F), Self((packed >> 4) & 0x0F))
}
}