Skip to main content

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}