urna_format/encoding/float16.rs
1//! float16 conversions for the `encoding=2` embeddings path. The
2//! runtime never accumulates in f16 - quantize at write time, decode
3//! lane-by-lane into f32 at read time.
4
5/// Convert an L2-normalized float32 embedding to float16, returning the
6/// little-endian byte representation. Not a checked NaN/Inf path -
7/// callers must validate inputs upstream.
8pub fn f32_to_f16_bytes(values: &[f32]) -> Vec<u8> {
9 let mut out = Vec::with_capacity(values.len() * 2);
10 for &v in values {
11 let half = half::f16::from_f32(v);
12 out.extend_from_slice(&half.to_le_bytes());
13 }
14 out
15}
16
17/// Decode a float16 byte slice into f32 values, accumulating in f32
18/// for downstream dot-product accuracy.
19#[inline]
20pub fn f16_bytes_to_f32(bytes: &[u8]) -> Vec<f32> {
21 let mut out = Vec::with_capacity(bytes.len() / 2);
22 for chunk in bytes.chunks_exact(2) {
23 let h = half::f16::from_le_bytes([chunk[0], chunk[1]]);
24 out.push(h.to_f32());
25 }
26 out
27}