#[inline]
pub const fn largest_code(bits: u32) -> i32 {
(1_i32 << (bits - 1)) - 1
}
#[inline]
pub const fn smallest_code(bits: u32) -> i32 {
-(1_i32 << (bits - 1))
}
#[inline]
pub fn symmetric_scale(max_abs: f32, bits: u32) -> f32 {
if max_abs > 0.0 {
max_abs / largest_code(bits) as f32
} else {
1.0
}
}
#[inline]
pub fn asymmetric_params(lowest: f32, highest: f32, bits: u32) -> (f32, f32) {
if lowest >= highest {
return (1.0, 0.0);
}
let code_min = smallest_code(bits) as f32;
let code_max = largest_code(bits) as f32;
let scale = (highest - lowest) / (code_max - code_min);
let zero_point = code_min - lowest / scale;
(scale, zero_point)
}
pub fn choose_bits(range: f32, tolerance: f32) -> u32 {
if range <= 0.0 {
return 2;
}
for bits in 2..=8 {
let tick_count = ((1u32 << bits) - 1) as f32;
let half_step = range / tick_count / 2.0;
if half_step <= tolerance {
return bits;
}
}
8
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn four_bit_codes_run_from_minus_eight_to_seven() {
assert_eq!(largest_code(4), 7);
assert_eq!(smallest_code(4), -8);
}
#[test]
fn choose_bits_picks_two_for_tiny_range() {
assert_eq!(choose_bits(0.001, 0.001), 2);
}
#[test]
fn choose_bits_saturates_at_eight() {
assert_eq!(choose_bits(10.0, 0.0001), 8);
}
}