//! Fixed point numbers. use std::{fmt::{self, Write}, ops}; macro_rules! define_fixed_type { ( $(#[$outer:meta])* struct $Type:ident ($IT:ident) : $UT:ident, $LT:ident, $FracBits:expr; ) => { $(#[$outer])* #[derive(Clone, PartialEq, serde::Serialize)] pub struct $Type($IT); impl $Type { /// The number of fractional bits in this type. pub const FRACBITS: $UT = $FracBits; /// The integer mask for the fractional bits. pub const FRACMASK: $UT = (1 << Self::FRACBITS) - 1; /// The representation of `1.0` in this type. pub const ONE: $IT = 1 << Self::FRACBITS; /// Creates a value of this type with the bit pattern `bits`. #[inline] pub const fn from_bits(bits: $UT) -> Self {$Type(bits as $IT)} /// Returns the raw bit pattern. #[inline] pub fn to_bits(&self) -> $UT {self.0 as $UT} /// Sets the raw bit pattern to `bits`. #[inline] pub fn set_bits(&mut self, bits: $UT) {self.0 = bits as $IT} /// Returns the integral portion. #[inline] pub fn integ(&self) -> $LT {(self.0 >> Self::FRACBITS) as $LT} /// Returns the fractional portion. /// /// This is a value of `1` to `FRACMASK`. #[allow(trivial_numeric_casts)] pub fn fract(&self) -> u16 {(self.0 as $UT & Self::FRACMASK) as u16} /// Returns a multiplication by integer. /// /// Panics if the result overflows. #[inline] pub fn mul_i(&self, n: $LT) -> Self {$Type(self.0 * $IT::from(n))} /// Returns a division by integer. /// /// Panics if `n` is `0`. #[inline] pub fn div_i(&self, n: $LT) -> Self {$Type(self.0 / $IT::from(n))} #[inline] fn fx_div(x: $IT, y: $IT) -> $IT { (i64::from(x) * i64::from(Self::ONE) / i64::from(y)) as $IT } #[inline] fn fx_mul(x: $IT, y: $IT) -> $IT { (i64::from(x) * i64::from(y) / i64::from(Self::ONE)) as $IT } } impl From<$LT> for $Type { #[inline] fn from(n: $LT) -> Self {$Type($IT::from(n) << Self::FRACBITS)} } impl ops::Add for $Type { type Output = Self; #[inline] fn add(self, o: Self) -> Self {$Type(self.0 + o.0)} } impl ops::Sub for $Type { type Output = Self; #[inline] fn sub(self, o: Self) -> Self {$Type(self.0 - o.0)} } impl ops::Mul for $Type { type Output = Self; #[inline] fn mul(self, o: Self) -> Self {$Type(Self::fx_mul(self.0, o.0))} } impl ops::Div for $Type { type Output = Self; #[inline] fn div(self, o: Self) -> Self {$Type(Self::fx_div(self.0, o.0))} } impl ops::Neg for $Type { type Output = Self; #[inline] fn neg(self) -> Self {$Type(-self.0)} } impl ops::Not for $Type { type Output = Self; #[inline] fn not(self) -> Self {$Type(!self.0)} } impl fmt::Display for $Type { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let prec = f.precision().unwrap_or(1); let widt = f.width().unwrap_or(0); write!(f, "{:widt$}.", self.integ(), widt = widt)?; let mut k = self.to_bits(); for _ in 0..prec { k &= Self::FRACMASK; k *= 10; let d = k >> Self::FRACBITS; let d = d % 10; f.write_char((d as u8 + b'0') as char)?; } Ok(()) } } impl fmt::Debug for $Type { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(self, f) } } }; } define_fixed_type! { /// A fixed point type representing an angle. /// /// The format of this type is `0.9s`, but because of the implementation, /// the real format is `7.9s`. struct Angle(i16) : u16, i8, 9; } define_fixed_type! { /// A fixed point type representing a world unit. /// /// The format of this type is `5.10s`. This has caused eternal suffering. struct Unit(i16) : u16, i8, 10; } define_fixed_type! { /// A generic fixed point type. /// /// The format of this type is `15.16s`. struct Fixed(i32) : u32, i16, 16; } #[test] fn fixed_basic_ops() { let seven_div_2 = 3 << Fixed::FRACBITS | Fixed::FRACMASK / 2 + 1; assert_eq!((Fixed::from(1) + 1.into()).to_bits(), 2 << Fixed::FRACBITS); assert_eq!((Fixed::from(2) - 1.into()).to_bits(), 1 << Fixed::FRACBITS); assert_eq!((Fixed::from(6) * 2.into()).to_bits(), 12 << Fixed::FRACBITS); assert_eq!((Fixed::from(6).mul_i(2)).to_bits(), 12 << Fixed::FRACBITS); assert_eq!((Fixed::from(7) / 2.into()).to_bits(), seven_div_2); assert_eq!((Fixed::from(7).div_i(2)).to_bits(), seven_div_2); } #[test] #[should_panic] #[allow(unused_must_use)] fn fixed_overflow() {Fixed::from(i16::max_value()) + 1.into();} #[test] fn fixed_printing() { assert_eq!(format!("{}", Fixed::from(6)), "6.0"); assert_eq!(format!("{}", Fixed::from(7).div_i(2)), "3.5"); assert_eq!(format!("{:7.7}", Fixed::from_bits(0xDEAD_BEEF)), " -8531.7458343"); } // EOF