pub const F16_MIN_NORMAL: f32 = 6.1e-5;
pub const F16_SMALLEST_NORMAL_BITS: u16 = 0x0400;
#[inline]
#[must_use]
pub fn safe_f16_scale(val: f32) -> f32 {
if val.is_nan() || val.is_infinite() {
0.0
} else {
val
}
}
#[inline]
#[must_use]
pub fn is_safe_f16_scale(val: f32) -> bool {
val.is_finite() && val.abs() >= F16_MIN_NORMAL
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_f16_min_normal_value() {
assert!(F16_MIN_NORMAL > 0.0);
assert!(F16_MIN_NORMAL < 1e-4);
assert!((F16_MIN_NORMAL - 6.1e-5).abs() < 1e-10);
}
#[test]
fn test_f16_smallest_normal_bits() {
assert_eq!(F16_SMALLEST_NORMAL_BITS, 0x0400);
}
#[test]
fn test_safe_f16_scale_normal_preserved() {
assert_eq!(safe_f16_scale(1.0), 1.0);
assert_eq!(safe_f16_scale(0.5), 0.5);
assert_eq!(safe_f16_scale(0.001), 0.001);
assert_eq!(safe_f16_scale(F16_MIN_NORMAL), F16_MIN_NORMAL);
}
#[test]
fn test_safe_f16_scale_nan_clamped() {
assert_eq!(safe_f16_scale(f32::NAN), 0.0);
}
#[test]
fn test_safe_f16_scale_inf_clamped() {
assert_eq!(safe_f16_scale(f32::INFINITY), 0.0);
assert_eq!(safe_f16_scale(f32::NEG_INFINITY), 0.0);
}
#[test]
fn test_safe_f16_scale_subnormal_preserved() {
assert_eq!(safe_f16_scale(1e-6), 1e-6);
assert_eq!(safe_f16_scale(1e-8), 1e-8);
let half = F16_MIN_NORMAL * 0.5;
assert_eq!(safe_f16_scale(half), half);
}
#[test]
fn test_safe_f16_scale_zero_preserved() {
assert_eq!(safe_f16_scale(0.0), 0.0);
assert_eq!(safe_f16_scale(-0.0), 0.0);
}
#[test]
fn test_safe_f16_scale_negative_normal_preserved() {
assert_eq!(safe_f16_scale(-1.0), -1.0);
assert_eq!(safe_f16_scale(-0.5), -0.5);
}
#[test]
fn test_safe_f16_scale_boundary() {
let just_above = F16_MIN_NORMAL * 1.01;
assert_eq!(safe_f16_scale(just_above), just_above);
let just_below = F16_MIN_NORMAL * 0.99;
assert_eq!(safe_f16_scale(just_below), just_below);
}
#[test]
fn test_is_safe_f16_scale_normal() {
assert!(is_safe_f16_scale(1.0));
assert!(is_safe_f16_scale(0.5));
assert!(is_safe_f16_scale(F16_MIN_NORMAL));
assert!(is_safe_f16_scale(-1.0));
}
#[test]
fn test_is_safe_f16_scale_invalid_values() {
assert!(!is_safe_f16_scale(f32::NAN));
assert!(!is_safe_f16_scale(f32::INFINITY));
assert!(!is_safe_f16_scale(f32::NEG_INFINITY));
assert!(!is_safe_f16_scale(0.0));
assert!(!is_safe_f16_scale(1e-6));
}
#[test]
fn test_gh186_boundary_0x0400_smallest_normal() {
let smallest_normal_f32 = 2.0_f32.powi(-14);
assert!(
smallest_normal_f32 >= F16_MIN_NORMAL,
"0x0400 ({smallest_normal_f32}) should be >= F16_MIN_NORMAL ({F16_MIN_NORMAL})"
);
assert_eq!(
safe_f16_scale(smallest_normal_f32),
smallest_normal_f32,
"Smallest normal f16 should be preserved"
);
}
#[test]
fn test_gh186_boundary_0x03ff_largest_subnormal() {
let largest_subnormal_f32 = 2.0_f32.powi(-14) * (1.0 - 2.0_f32.powi(-10));
assert!(
largest_subnormal_f32 < F16_MIN_NORMAL,
"0x03FF ({largest_subnormal_f32}) should be < F16_MIN_NORMAL ({F16_MIN_NORMAL})"
);
assert_eq!(
safe_f16_scale(largest_subnormal_f32),
largest_subnormal_f32,
"Largest subnormal f16 should be preserved (PMAT-238)"
);
}
}