pub const CENTROIDS_4BIT: [f32; 16] = [
-1.996_112, -1.512_225, -1.172_563, -0.887_568, -0.632_509, -0.395_530, -0.169_891, 0.049_892,
0.269_673, 0.495_312, 0.732_291, 0.987_350, 1.272_345, 1.612_007, 2.095_894, 2.724_265,
];
pub const BOUNDARIES_4BIT: [f32; 15] = [
-1.751_289, -1.340_438, -1.026_996, -0.756_128, -0.509_062, -0.276_322, -0.056_279, 0.164_289,
0.391_029, 0.626_095, 0.868_960, 1.125_026, 1.413_956, 1.764_827, 2.238_297,
];
#[inline]
pub fn quantize_4bit(x: f32) -> u8 {
let mut lo = 0usize;
let mut hi = BOUNDARIES_4BIT.len(); while lo < hi {
let mid = (lo + hi) / 2;
if x > BOUNDARIES_4BIT[mid] {
lo = mid + 1;
} else {
hi = mid;
}
}
lo as u8
}
#[inline]
pub fn dequantize_4bit(i: u8) -> f32 {
CENTROIDS_4BIT[i as usize]
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn centroids_are_sorted() {
for w in CENTROIDS_4BIT.windows(2) {
assert!(w[0] < w[1], "centroids must be strictly increasing");
}
}
#[test]
fn boundaries_are_sorted_and_interleaved() {
for w in BOUNDARIES_4BIT.windows(2) {
assert!(w[0] < w[1]);
}
for i in 0..15 {
assert!(CENTROIDS_4BIT[i] < BOUNDARIES_4BIT[i]);
assert!(BOUNDARIES_4BIT[i] < CENTROIDS_4BIT[i + 1]);
}
}
#[test]
fn extreme_values_map_to_endpoints() {
assert_eq!(quantize_4bit(-10.0), 0);
assert_eq!(quantize_4bit(10.0), 15);
}
#[test]
fn zero_maps_near_center() {
let i = quantize_4bit(0.0);
assert!(
(7..=8).contains(&i),
"0.0 should map to centroid 7 or 8, got {i}"
);
}
#[test]
fn round_trip_error_is_bounded() {
let mut max_gap = 0f32;
for i in 0..15 {
max_gap = max_gap.max(CENTROIDS_4BIT[i + 1] - CENTROIDS_4BIT[i]);
}
assert!(max_gap < 0.75, "max centroid gap {max_gap} too large");
}
}