Q format represents fractional values with fixed-point scaling rather than a moving decimal place. Q7 stores values from approximately −1 to 1 in one signed byte: seven value bits plus one sign bit. section 5.5
A Q7 implementation:
- Wraps an i8 in a Rust tuple struct, allowing the compiler to track the sign and derive comparison-related traits. section 5.5
- Converts f64 values by multiplying by 128 and storing the result as i8. section 5.5
- Clamps values at or beyond the boundaries to Q7(127) and Q7(-128), then converts back using a 2⁻⁷ scale. section 5.5
Converting a value
A value such as 0.5 becomes Q7(64), because 0.5 × 128 equals 64; converting it back multiplies 64 by 2⁻⁷. section 5.5
Listing 5.14 Full code implementation of the Q7 format
#[derive(Debug,Clone,Copy,PartialEq,Eq)]
pub struct Q7(i8);
impl From<f64> for Q7 {
fn from (n: f64) -> Self {
if n >= 1.0 {
Q7(127)
} else if n <= -1.0 {
Q7(-128)
} else {
Q7((n * 128.0) as i8)
}
}
}
impl From<Q7> for f64 {
fn from(n: Q7) -> f64 {
(n.0 as f64) * 2f64.powf(-7.0)
}
}
impl From<f32> for Q7 {
fn from (n: f32) -> Self {
Q7::from(n as f64)
}
}
impl From<Q7> for f32 {
fn from(n: Q7) -> f32 {
f64::from(n) as f32
}
}
#[cfg(test)]
mod tests { #1
use super::*; #2
#[test]
fn out_of_bounds() {
assert_eq!(Q7::from(10.), Q7::from(1.));
assert_eq!(Q7::from(-10.), Q7::from(-1.));
}
#[test]
fn f32_to_q7() {
let n1: f32 = 0.7;
let q1 = Q7::from(n1);
let n2 = -0.4;
* let q2 = Q7::from(n2);
let n3 = 123.0;
let q3 = Q7::from(n3);
assert_eq!(q1, Q7(89));
assert_eq!(q2, Q7(-51));
assert_eq!(q3, Q7(127));
}
#[test]
fn q7_to_f32() {
let q1 = Q7::from(0.7);
let n1 = f32::from(q1);
assert_eq!(n1, 0.6953125);
let q2 = Q7::from(n1);
let n2 = f32::from(q2);
assert_eq!(n1, n2);
}
}
Inspect the complete Q7 implementation, including conversions for f32 and f64 and tests for bounds and round trips. section 5.5
