#[inline]
pub fn is_half_inf_or_nan(h: u16) -> bool {
(h >> 10) & 0x1F == 0x1F
}
pub fn half_from_unorm16(k: u32) -> u16 {
debug_assert!(k <= 0xFFFF);
if k == 0 {
return 0;
}
let n = 31 - k.leading_zeros();
if n < 2 {
return (k << 8) as u16;
}
let m = if n >= 10 {
(k >> (n - 10)) & 0x3FF
} else {
(k << (10 - n)) & 0x3FF
};
(((n - 1) << 10) | m) as u16
}
pub fn half_to_float(h: u16) -> f32 {
let s = (h as u32 >> 15) & 1;
let mut e = (h as u32 >> 10) & 0x1F;
let mut m = h as u32 & 0x3FF;
if e == 0 {
if m == 0 {
return f32::from_bits(s << 31);
}
while m & 0x400 == 0 {
m <<= 1;
e = e.wrapping_sub(1);
}
e = e.wrapping_add(1);
m &= !0x400;
} else if e == 31 {
if m == 0 {
return f32::from_bits((s << 31) | 0x7F80_0000);
}
return f32::from_bits((s << 31) | 0x7F80_0000 | (m << 13));
}
let e = e.wrapping_add(127 - 15);
f32::from_bits((m << 13) | (e << 23) | (s << 31))
}
#[inline]
pub fn qlog16_to_half(k: i32) -> u16 {
debug_assert!((0..=0xFFFF).contains(&k));
let e = (k & 0xF800) >> 11;
let m = k & 0x7FF;
let mt = if m < 512 {
3 * m
} else if m >= 1536 {
5 * m - 2048
} else {
4 * m - 512
};
((e << 10) + (mt >> 3)) as u16
}
#[cfg(test)]
mod tests {
use super::*;
fn float_to_half_toward_zero(val: f32) -> u16 {
let bits = val.to_bits();
let flt_m = bits & 0x7F_FFFF;
let flt_e = (bits >> 23) & 0xFF;
assert_eq!(bits >> 31, 0);
assert_ne!(flt_e, 0xFF);
let (mut e, mut m) = (0u32, 0u32);
if flt_e != 0 {
let new_exp = flt_e as i32 - 127;
if new_exp > 15 {
e = 31;
} else if new_exp < -14 {
let sig = (flt_m | 0x80_0000) as u64;
let shift = 24 + new_exp - 23; m = if shift >= 0 {
(sig << shift) as u32
} else {
(sig >> -shift) as u32
};
} else {
e = (new_exp + 15) as u32;
m = flt_m >> 13; }
}
if m == 1024 {
e += 1;
m = 0;
}
((e << 10) | m) as u16
}
#[test]
fn half_from_unorm16_matches_reference_formula() {
for k in 0u32..=0xFFFF {
let f = k as f32 * (1.0 / 65536.0);
assert_eq!(half_from_unorm16(k), float_to_half_toward_zero(f), "k={k}");
}
}
#[test]
fn half_to_float_matches_ieee_values() {
for h in 0u16..0x7C00 {
let f = half_to_float(h);
let (e, m) = ((h as u32 >> 10) & 0x1F, h as u32 & 0x3FF);
let want = if e == 0 {
m as f32 * f32::from_bits((127 - 24) << 23)
} else {
f32::from_bits(((e + 112) << 23) | (m << 13))
};
assert_eq!(f.to_bits(), want.to_bits(), "h={h:#06x}");
}
}
}