use crate::quant::ScaledFormat;
#[inline]
pub fn decode(fmt: ScaledFormat, code: u8) -> f32 {
if matches!(fmt, ScaledFormat::F4E2M1) {
return crate::nvfp4::fp4_e2m1_to_f32(code);
}
let (e_bits, m_bits, bias) = fmt.fields();
let width = e_bits + m_bits; let sign_bit = (code >> width) & 1;
let exp = (u32::from(code) >> m_bits) & ((1u32 << e_bits) - 1);
let mant = u32::from(code) & ((1u32 << m_bits) - 1);
let sign = if sign_bit == 1 { -1.0f32 } else { 1.0f32 };
let max_exp = (1u32 << e_bits) - 1;
if fmt.is_fnuz() {
if sign_bit == 1 && exp == 0 && mant == 0 {
return f32::NAN;
}
} else if fmt.has_inf() {
if exp == max_exp {
return if mant == 0 {
sign * f32::INFINITY
} else {
f32::NAN
};
}
} else if matches!(fmt, ScaledFormat::F8E4M3) {
if exp == max_exp && mant == (1u32 << m_bits) - 1 {
return f32::NAN;
}
}
let m_div = (1u32 << m_bits) as f32;
let val = if exp == 0 {
(mant as f32 / m_div) * 2f32.powi(1 - bias)
} else {
(1.0 + mant as f32 / m_div) * 2f32.powi(exp as i32 - bias)
};
sign * val
}
#[inline]
pub fn encode(fmt: ScaledFormat, x: f32) -> u8 {
if x.is_nan() {
return 0;
}
let n_codes: u16 = 1 << fmt.bit_width();
let mut best: u8 = 0;
let mut best_err = f64::INFINITY;
let mut best_mant_lsb: u8 = 1; let xd = x as f64;
for c in 0..n_codes {
let code = c as u8;
let v = decode(fmt, code);
if !v.is_finite() {
continue; }
let err = (v as f64 - xd).abs();
let mant_lsb = code & 1;
if err < best_err || (err == best_err && mant_lsb < best_mant_lsb) {
best_err = err;
best = code;
best_mant_lsb = mant_lsb;
}
}
best
}
pub fn max_finite(fmt: ScaledFormat) -> f32 {
let n_codes: u16 = 1 << fmt.bit_width();
(0..n_codes)
.map(|c| decode(fmt, c as u8).abs())
.filter(|v| v.is_finite())
.fold(0.0f32, f32::max)
}
pub fn decode_slice(fmt: ScaledFormat, codes: &[u8], out: &mut [f32]) {
debug_assert_eq!(codes.len(), out.len());
for (o, &c) in out.iter_mut().zip(codes) {
*o = decode(fmt, c);
}
}
#[inline]
pub fn f32_to_e8m0(scale: f32) -> u8 {
if !scale.is_finite() || scale <= 0.0 {
return 0; }
let mut exp = scale.log2().ceil() as i32 + 127;
exp = exp.clamp(0, 254);
exp as u8
}
#[inline]
pub fn e8m0_to_f32(byte: u8) -> f32 {
if byte == 0xFF {
return f32::NAN;
}
2f32.powi(byte as i32 - 127)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::quant::ScaledFormat::*;
const ALL: [ScaledFormat; 7] = [
F8E4M3, F8E5M2, F8E4M3Fnuz, F8E5M2Fnuz, F6E2M3, F6E3M2, F4E2M1,
];
#[test]
fn round_trip_every_representable_code() {
for fmt in ALL {
let n = 1u16 << fmt.bit_width();
for c in 0..n {
let v = decode(fmt, c as u8);
if !v.is_finite() {
continue;
}
let re = decode(fmt, encode(fmt, v));
assert_eq!(
re, v,
"{fmt}: code {c:#04x} decoded {v}, re-encoded to {re}"
);
}
}
}
#[test]
fn known_max_finite() {
assert_eq!(max_finite(F8E4M3), 448.0);
assert_eq!(max_finite(F8E5M2), 57344.0);
assert_eq!(max_finite(F8E4M3Fnuz), 240.0);
assert_eq!(max_finite(F8E5M2Fnuz), 57344.0);
assert_eq!(max_finite(F6E2M3), 7.5);
assert_eq!(max_finite(F6E3M2), 28.0);
assert_eq!(max_finite(F4E2M1), 6.0);
}
#[test]
fn unity_decodes_to_one() {
for fmt in ALL {
let (e, m, bias) = fmt.fields();
let code = ((bias as u32) << m) as u8;
let _ = e;
assert_eq!(decode(fmt, code), 1.0, "{fmt}: 1.0 code {code:#04x}");
assert_eq!(decode(fmt, encode(fmt, 1.0)), 1.0, "{fmt}");
}
}
#[test]
fn e4m3_has_one_nan_no_inf() {
assert!(decode(F8E4M3, 0x7F).is_nan());
assert!(decode(F8E4M3, 0xFF).is_nan());
let finite = (0..256u16)
.filter(|&c| decode(F8E4M3, c as u8).is_finite())
.count();
assert_eq!(finite, 254);
}
#[test]
fn e5m2_has_inf_and_nan() {
assert_eq!(decode(F8E5M2, 0x7C), f32::INFINITY); assert_eq!(decode(F8E5M2, 0xFC), f32::NEG_INFINITY);
assert!(decode(F8E5M2, 0x7D).is_nan());
}
#[test]
fn fnuz_single_nan_no_neg_zero() {
assert!(decode(F8E4M3Fnuz, 0x80).is_nan());
assert!(decode(F8E5M2Fnuz, 0x80).is_nan());
assert_eq!(decode(F8E4M3Fnuz, 0x00), 0.0);
assert!(!(0..256u16).any(|c| decode(F8E4M3Fnuz, c as u8).is_infinite()));
}
#[test]
fn saturates_on_overflow() {
for fmt in ALL {
let big = 1.0e9;
let v = decode(fmt, encode(fmt, big));
assert!(v.is_finite(), "{fmt} overflow not finite");
assert_eq!(v, max_finite(fmt), "{fmt} did not saturate to max");
}
}
#[test]
fn e8m0_round_trips_powers_of_two() {
for p in -10..=10i32 {
let s = 2f32.powi(p);
assert_eq!(e8m0_to_f32(f32_to_e8m0(s)), s, "2^{p}");
}
}
}