/// Fixed-point arithmetic with configurable fractional bits. /// /// All operations use i64 internally with i128 intermediates /// for multiply to avoid overflow. Shift-and-add only. #[derive(Debug, Clone, Copy)] pub struct FixedPoint { pub frac_bits: u8, } impl FixedPoint { pub fn new(frac_bits: u8) -> Self { Self { frac_bits } } #[inline] pub fn one(&self) -> i64 { 1i64 << self.frac_bits } #[inline] pub fn from_f64(&self, val: f64) -> i64 { (val * (1i64 << self.frac_bits) as f64).round() as i64 } #[inline] pub fn to_f64(&self, val: i64) -> f64 { val as f64 / (1i64 << self.frac_bits) as f64 } #[inline] pub fn mul(&self, a: i64, b: i64) -> i64 { ((a as i128 * b as i128) >> self.frac_bits) as i64 } #[inline] pub fn div(&self, a: i64, b: i64) -> i64 { if b == 0 { return if a >= 0 { i64::MAX } else { i64::MIN }; } (((a as i128) << self.frac_bits) / b as i128) as i64 } } #[cfg(test)] mod tests { use super::*; #[test] fn mul_basic() { let fp = FixedPoint::new(16); let a = fp.from_f64(2.5); let b = fp.from_f64(4.0); let result = fp.to_f64(fp.mul(a, b)); assert!((result - 10.0).abs() < 0.001); } #[test] fn div_basic() { let fp = FixedPoint::new(16); let a = fp.from_f64(10.0); let b = fp.from_f64(4.0); let result = fp.to_f64(fp.div(a, b)); assert!((result - 2.5).abs() < 0.001); } }