use half::bf16;
const QUIET_NAN_F32: u32 = 0x7fc0_0000;
const QUIET_NAN_BF16: u16 = 0x7fc0;
pub(crate) fn bf16_to_f32(v: bf16) -> f32 {
if v.is_nan() {
f32::from_bits(QUIET_NAN_F32)
} else if v.is_finite() && !v.is_normal() {
if v.is_sign_positive() { 0.0 } else { -0.0 }
} else {
v.to_f32()
}
}
pub(crate) fn f32_to_bf16(v: f32) -> bf16 {
if v.is_nan() {
bf16::from_bits(QUIET_NAN_BF16)
} else if v.is_subnormal() {
if v.is_sign_positive() {
bf16::ZERO
} else {
bf16::NEG_ZERO
}
} else {
bf16::from_f32(v)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn subnormals_flush_to_signed_zero() {
assert_eq!(f32_to_bf16(f32::MIN_POSITIVE / 2.0).to_bits(), bf16::ZERO.to_bits());
assert_eq!(
f32_to_bf16(-f32::MIN_POSITIVE / 2.0).to_bits(),
bf16::NEG_ZERO.to_bits()
);
assert_ne!(f32_to_bf16(f32::MIN_POSITIVE).to_bits(), bf16::ZERO.to_bits());
let sub = bf16::from_bits(0x0001);
assert!(sub.is_finite() && !sub.is_normal());
assert_eq!(bf16_to_f32(sub).to_bits(), 0.0f32.to_bits());
assert_eq!(bf16_to_f32(-sub).to_bits(), (-0.0f32).to_bits());
}
#[test]
fn nan_canonicalizes_in_both_directions() {
assert_eq!(f32_to_bf16(f32::from_bits(0x7fa0_0001)).to_bits(), QUIET_NAN_BF16);
assert_eq!(bf16_to_f32(bf16::from_bits(0x7f81)).to_bits(), QUIET_NAN_F32);
}
#[test]
fn zero_and_normal_pass_through() {
assert_eq!(f32_to_bf16(-0.0).to_bits(), bf16::NEG_ZERO.to_bits());
assert_eq!(f32_to_bf16(1.5).to_bits(), bf16::from_f32(1.5).to_bits());
assert_eq!(bf16_to_f32(bf16::from_f32(-2.5)), -2.5);
}
}