#![allow(clippy::float_cmp)]
use microfloat::f8e4m3fn;
use std::num::FpCategory;
const fn e4m3_bits(sign: u8, exp: u8, mant: u8) -> u8 {
(sign << 7) | ((exp & 0xF) << 3) | (mant & 0x7)
}
#[test]
fn e4m3_bias_is_7() {
assert_eq!(f8e4m3fn::ONE.to_bits(), e4m3_bits(0b0, 0b0111, 0b000));
assert_eq!(
f8e4m3fn::from_f32(2.0).to_bits(),
e4m3_bits(0b0, 0b1000, 0b000)
);
assert_eq!(
f8e4m3fn::from_f32(0.5).to_bits(),
e4m3_bits(0b0, 0b0110, 0b000)
);
}
#[test]
fn e4m3_exponent_parameters() {
assert_eq!(f8e4m3fn::ONE.to_bits(), e4m3_bits(0b0, 0b0111, 0b000));
}
#[test]
fn e4m3_zeros() {
assert_eq!(f8e4m3fn::ZERO.to_bits(), e4m3_bits(0b0, 0b0000, 0b000));
assert_eq!(f8e4m3fn::NEG_ZERO.to_bits(), e4m3_bits(0b1, 0b0000, 0b000));
assert!(f8e4m3fn::ZERO.to_f32() == 0.0);
assert!(f8e4m3fn::NEG_ZERO.is_sign_negative());
}
#[test]
fn e4m3_max_normal_number() {
assert_eq!(f8e4m3fn::MAX.to_bits(), e4m3_bits(0b0, 0b1111, 0b110));
assert_eq!(f8e4m3fn::MAX.to_f32(), 448.0);
}
#[test]
fn e4m3_exact_max_normal_448() {
let bits = e4m3_bits(0b0, 0b1111, 0b110);
let val = f8e4m3fn::from_bits(bits);
assert_eq!(val.to_f32(), 448.0, "max finite should be exactly 448");
}
#[test]
fn e4m3_min_normal_number() {
let min_normal = f8e4m3fn::from_f32(2.0f32.powi(-6));
assert_eq!(min_normal.to_f32(), 2.0f32.powi(-6));
assert_eq!(min_normal.to_bits(), e4m3_bits(0b0, 0b0001, 0b000));
}
#[test]
fn e4m3_exact_min_normal_value() {
let bits = e4m3_bits(0b0, 0b0001, 0b000);
let val = f8e4m3fn::from_bits(bits);
assert_eq!(
val.to_f32(),
2.0_f32.powi(-6),
"min normal should be exactly 2^-6"
);
}
#[test]
fn e4m3_max_subnormal_number() {
let bits = e4m3_bits(0b0, 0b0000, 0b111);
let max_sub = f8e4m3fn::from_bits(bits);
let expected = 7.0 * 2.0_f32.powi(-9);
assert_eq!(max_sub.to_f32(), expected);
}
#[test]
fn e4m3_exact_max_subnormal_7_2neg9() {
let bits = e4m3_bits(0b0, 0b0000, 0b111);
let val = f8e4m3fn::from_bits(bits);
let expected = 7.0 * 2.0_f32.powi(-9);
assert_eq!(
val.to_f32(),
expected,
"max subnormal should be exactly 7*2^-9"
);
}
#[test]
fn e4m3_min_subnormal_number() {
let bits = e4m3_bits(0b0, 0b0000, 0b001);
let min_sub = f8e4m3fn::from_bits(bits);
assert_eq!(min_sub.to_f32(), 2.0_f32.powi(-9));
}
#[test]
fn e4m3_exact_min_subnormal() {
let bits = e4m3_bits(0b0, 0b0000, 0b001);
let val = f8e4m3fn::from_bits(bits);
assert_eq!(
val.to_f32(),
2.0_f32.powi(-9),
"min subnormal should be exactly 2^-9"
);
}
#[test]
fn e4m3_no_infinity() {
assert!(!f8e4m3fn::has_inf());
}
#[test]
fn e4m3_overflow_becomes_nan() {
let overflow = f8e4m3fn::from_f32(f32::INFINITY);
assert!(overflow.to_f32().is_nan());
assert!(f8e4m3fn::from_f32(500.0).to_f32().is_nan());
}
#[test]
fn e4m3_nan() {
assert!(f8e4m3fn::has_nan());
let nan_bits = e4m3_bits(0b0, 0b1111, 0b111);
let nan = f8e4m3fn::from_bits(nan_bits);
assert!(nan.to_f32().is_nan());
assert!(f8e4m3fn::NAN.to_f32().is_nan());
assert_eq!(f8e4m3fn::NAN.to_bits(), e4m3_bits(0b0, 0b1111, 0b111));
}
#[test]
fn e4m3_nan_bit_pattern() {
assert!(f8e4m3fn::NAN.to_f32().is_nan());
assert_eq!(f8e4m3fn::NAN.to_bits(), e4m3_bits(0b0, 0b1111, 0b111));
assert!(
f8e4m3fn::from_bits(e4m3_bits(0b0, 0b1111, 0b111))
.to_f32()
.is_nan()
);
assert!(
f8e4m3fn::from_bits(e4m3_bits(0b1, 0b1111, 0b111))
.to_f32()
.is_nan()
);
assert!(
!f8e4m3fn::from_bits(e4m3_bits(0b0, 0b1111, 0b110))
.to_f32()
.is_nan()
); assert!(
!f8e4m3fn::from_bits(e4m3_bits(0b0, 0b1111, 0b101))
.to_f32()
.is_nan()
); }
#[test]
fn e4m3_dynamic_range_18_binades() {
assert!(f8e4m3fn::from_f32(448.0).to_bits() == e4m3_bits(0b0, 0b1111, 0b110));
assert!(f8e4m3fn::from_f32(2.0f32.powi(-9)).to_bits() == e4m3_bits(0b0, 0b0000, 0b001));
}
#[test]
fn e4m3_bit_layout() {
assert_eq!(f8e4m3fn::ONE.to_bits(), e4m3_bits(0b0, 0b0111, 0b000));
assert_eq!(
f8e4m3fn::from_f32(-1.0).to_bits(),
e4m3_bits(0b1, 0b0111, 0b000)
);
assert_eq!(
f8e4m3fn::from_f32(448.0).to_bits(),
e4m3_bits(0b0, 0b1111, 0b110)
);
assert_eq!(
f8e4m3fn::from_f32(-448.0).to_bits(),
e4m3_bits(0b1, 0b1111, 0b110)
);
}
#[test]
fn e4m3_roundtrip_normal_values() {
let vals = [
1.0, -1.0, 2.0, -2.0, 4.0, -4.0, 0.5, -0.5, 0.25, -0.25, 8.0, -8.0, 16.0, -16.0, 32.0,
-32.0, 64.0, -64.0, 128.0, -128.0, 240.0, -240.0, 448.0, -448.0,
];
for &v in &vals {
let encoded = f8e4m3fn::from_f32(v);
let decoded = encoded.to_f32();
assert!(
(decoded - v).abs() / v.abs() < 1e-2,
"e4m3 roundtrip failed: {v} -> {decoded} (error {:.4}%)",
(decoded - v).abs() / v.abs() * 100.0
);
}
}
#[test]
fn e4m3_roundtrip_subnormal_values() {
let vals = [
2.0_f32.powi(-9), 2.0_f32.powi(-8),
3.0 * 2.0_f32.powi(-9),
7.0 * 2.0_f32.powi(-9), ];
for &v in &vals {
let encoded = f8e4m3fn::from_f32(v);
let decoded = encoded.to_f32();
assert!(
(decoded - v).abs() < 1e-12,
"e4m3 subnormal roundtrip failed: {v} -> {decoded}"
);
}
}
#[test]
fn e4m3_overflow_to_nan() {
assert!(f8e4m3fn::from_f32(500.0).to_f32().is_nan());
assert!(f8e4m3fn::from_f32(f32::INFINITY).to_f32().is_nan());
assert!(f8e4m3fn::from_f32(f32::NEG_INFINITY).to_f32().is_nan());
}
#[test]
fn e4m3_underflow_to_subnormal() {
let tiny = 0.5 * 2.0_f32.powi(-9); let encoded = f8e4m3fn::from_f32(tiny);
assert!(
encoded.to_bits() == e4m3_bits(0b0, 0b0000, 0b001)
|| encoded.to_bits() == e4m3_bits(0b0, 0b0000, 0b000)
);
}
#[test]
fn e4m3_classify_zero() {
assert_eq!(f8e4m3fn::ZERO.classify(), FpCategory::Zero);
assert_eq!(f8e4m3fn::NEG_ZERO.classify(), FpCategory::Zero);
assert_eq!(
f8e4m3fn::from_bits(e4m3_bits(0b0, 0b0000, 0b001)).classify(),
FpCategory::Subnormal
);
}
#[test]
fn e4m3_classify_subnormal() {
assert_eq!(
f8e4m3fn::from_bits(e4m3_bits(0b0, 0b0000, 0b001)).classify(),
FpCategory::Subnormal
);
assert_eq!(
f8e4m3fn::from_bits(e4m3_bits(0b0, 0b0000, 0b111)).classify(),
FpCategory::Subnormal
);
assert_eq!(
f8e4m3fn::from_bits(e4m3_bits(0b0, 0b0001, 0b000)).classify(),
FpCategory::Normal
);
assert_eq!(f8e4m3fn::ZERO.classify(), FpCategory::Zero);
}
#[test]
fn e4m3_classify_special() {
assert!(!f8e4m3fn::from_f32(448.0).is_infinite());
assert!(f8e4m3fn::NAN.is_nan());
assert!(!f8e4m3fn::ONE.is_infinite());
assert!(!f8e4m3fn::ONE.is_nan());
let max_val = f8e4m3fn::from_f32(448.0);
assert!(max_val.is_finite());
}