Printing an i64 as a fixed point 16

I have ah x: i64 that is representing a value x as real / 65536.0 (fixed point with 16 bits of decimal)

Is there an easy way to print this ? x as f64 / 65536.0 loses precision, and I'd prefer to not roll manual routines for handling integral / fractional parts

use std::fmt;

const BITS: u8 = 16;
const SCALE: i64 = 1 << BITS;
struct Fixed(i64);

impl fmt::Display for Fixed {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let integer_part = self.0 / SCALE;
        let fractional_part = (self.0 % SCALE).abs();
        
        // The fractional part is already multiplied by 2^BITS.
        // If we additionally multiply by 5^BITS, then it is now 
        // multiplied by 10^BITS, so if we put it BITS decimal places
        // to the right, it will have the correct place value.
        let mut decimal_fraction = fractional_part * 5i64.pow(BITS.into());
        
        // But we don't want to print extraneous zeros, so figure 
        // out how many places we actually need and adjust the value.
        let mut places: usize = BITS.into();
        while decimal_fraction % 10 == 0 && places > 1 {
            decimal_fraction /= 10;
            places -= 1;
        }
        
        write!(f, "{integer_part}.{decimal_fraction:0places$}")
    }
}

#[test]
fn test() {
    assert_eq!(Fixed(0).to_string(), "0.0");
    assert_eq!(Fixed(SCALE * 70).to_string(), "70.0");
    assert_eq!(Fixed(SCALE * 15 / 10).to_string(), "1.5");
    assert_eq!(Fixed(-(SCALE * 15 / 10)).to_string(), "-1.5");
    assert_eq!(Fixed(SCALE * 123125 / 1000).to_string(), "123.125");
}
5 Likes

A quick Google found the crate fixed which has fixed integer types that appear to implement Display.

I have no idea if this is the best crate for this purpose though.

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.