From 6e4c7512e861550b46abcb3fc53e2d9e68ad0342 Mon Sep 17 00:00:00 2001 From: Marrub Date: Mon, 4 Mar 2019 13:45:03 -0500 Subject: [PATCH] rewrite fixed point numbers to work better --- source/durandal/fixed.rs | 411 ++++++++++++++++++++++++++++++--------- 1 file changed, 321 insertions(+), 90 deletions(-) diff --git a/source/durandal/fixed.rs b/source/durandal/fixed.rs index ea5691d..b70a258 100644 --- a/source/durandal/fixed.rs +++ b/source/durandal/fixed.rs @@ -1,210 +1,441 @@ //! Fixed point numbers. -use std::{fmt::{self, Write}, - ops}; +use std::{fmt::{self, Write}, ops::*}; -macro_rules! define_fixed_type { +macro_rules! fixed_ref_unop { + (impl $imp:ident, $method:ident for $t:ty) => { + impl $imp for &$t + { + type Output = <$t as $imp>::Output; + + #[inline] + fn $method(self) -> <$t as $imp>::Output {$imp::$method(*self)} + } + }; +} + +macro_rules! fixed_ref_binop { + (impl $imp:ident, $method:ident for $t:ty, $u:ty) => { + impl<'a> $imp<$u> for &'a $t + { + type Output = <$t as $imp<$u>>::Output; + + #[inline] + fn $method(self, other: $u) -> <$t as $imp<$u>>::Output + { + $imp::$method(*self, other) + } + } + + impl<'a> $imp<&'a $u> for $t + { + type Output = <$t as $imp<$u>>::Output; + + #[inline] + fn $method(self, other: &'a $u) -> <$t as $imp<$u>>::Output + { + $imp::$method(self, *other) + } + } + + impl<'a, 'b> $imp<&'a $u> for &'b $t + { + type Output = <$t as $imp<$u>>::Output; + + #[inline] + fn $method(self, other: &'a $u) -> <$t as $imp<$u>>::Output + { + $imp::$method(*self, *other) + } + } + }; +} + +macro_rules! fixed_ref_op_assign { + (impl $imp:ident, $method:ident for $t:ty, $u:ty) => { + impl<'a> $imp<&'a $u> for $t + { + #[inline] + fn $method(&mut self, other: &'a $u) {$imp::$method(self, *other);} + } + }; +} + +macro_rules! define_fixed_types { ( - $(#[$outer:meta])* - struct $Type:ident ($IT:ident) : $UT:ident, $LT:ident, $FracBits:expr; - ) => { + $($(#[$outer:meta])* + struct $t:ident ($ti:ident, $bytes:expr) : $tu:ident, $frac_bits:expr;)* + ) => {$( $(#[$outer])* #[derive(Copy, Clone, Default, PartialEq, PartialOrd, serde::Serialize)] - pub struct $Type($IT); + pub struct $t($ti); - impl $Type + impl $t { /// The number of fractional bits in this type. - pub const FRACBITS: $UT = $FracBits; + #[inline] + pub const fn frac_bits() -> $tu {$frac_bits} + + /// The unsigned mask for the fractional bits. + #[inline] + pub const fn frac_mask() -> $tu {(1 << $t::frac_bits()) - 1} + /// The integer mask for the fractional bits. - pub const FRACMASK: $UT = (1 << Self::FRACBITS) - 1; + #[inline] + pub const fn frac_mask_i() -> $ti {(1 << $t::frac_bits()) - 1} + /// The representation of `1.0` in this type. - pub const ONE: $IT = 1 << Self::FRACBITS; + #[inline] + pub const fn one() -> $tu {1 << $t::frac_bits()} + + /// Returns the largest value that can be represented. + #[inline] + pub const fn max_value() -> $t {$t($ti::max_value())} + + /// Returns the smallest value that can be represented. + #[inline] + pub const fn min_value() -> $t {$t($ti::min_value())} + + /// Returns the integer part of a number. + #[inline] + pub const fn trunc(self) -> $t {$t(self.0 & !$t::frac_mask_i())} + + /// Returns the fractional part of a number. + #[inline] + pub const fn fract(self) -> $t {$t(self.0 & $t::frac_mask_i())} + + /// Returns the number of ones in the bit representation of self. + #[inline] + pub fn count_ones(self) -> u32 {self.0.count_ones()} + + /// Returns the number of zeros in the bit representation of self. + #[inline] + pub fn count_zeros(self) -> u32 {self.0.count_zeros()} + + /// Returns the number of leading zeros in the bit representation of + /// self. + #[inline] + pub fn leading_zeros(self) -> u32 {self.0.leading_zeros()} + + /// Returns the number of trailing zeros in the bit representation of + /// self. + #[inline] + pub fn trailing_zeros(self) -> u32 {self.0.trailing_zeros()} + + /// Rotates all bits left by `n`. + #[inline] + pub fn rotate_left(self, n: u32) -> $t {$t(self.0.rotate_left(n))} + + /// Rotates all bits right by `n`. + #[inline] + pub fn rotate_right(self, n: u32) -> $t {$t(self.0.rotate_right(n))} + + /// Reverses the byte order of the bit representation of self. + #[inline] + pub fn swap_bytes(self) -> $t {$t(self.0.swap_bytes())} + + /// Raises self to the power of `exp`. + #[inline] + pub fn pow(self, exp: u32) -> $t {$t(self.0.pow(exp))} + + /// Returns the absolute value of self. + #[inline] + pub fn abs(self) -> $t {$t(self.0.abs())} + + /// Returns a number representing sign of self. + #[inline] + pub fn signum(self) -> $t {$t(self.0.signum() << $t::frac_bits())} + + /// Returns true if self is positive and false if the number is zero + /// or negative. + #[inline] + pub fn is_positive(self) -> bool {self.0.is_positive()} + + /// Returns true if self is negative and false if the number is zero + /// or positive. + #[inline] + pub fn is_negative(self) -> bool {self.0.is_negative()} + + /// Return the memory representation of this integer as a byte array + /// in big-endian (network) byte order. + #[inline] + pub fn to_be_bytes(self) -> [u8; $bytes] {self.0.to_be_bytes()} + + /// Return the memory representation of this integer as a byte array + /// in little-endian byte order. + #[inline] + pub fn to_le_bytes(self) -> [u8; $bytes] {self.0.to_le_bytes()} + + /// Return the memory representation of this integer as a byte array + /// in native byte order. + #[inline] + pub fn to_ne_bytes(self) -> [u8; $bytes] {self.0.to_ne_bytes()} + + /// Create a value from its representation as a byte array in + /// big-endian byte order. + #[inline] + pub fn from_be_bytes(b: [u8; $bytes]) -> $t + { + $t($ti::from_be_bytes(b)) + } + + /// Create a value from its representation as a byte array in + /// little-endian byte order. + #[inline] + pub fn from_le_bytes(b: [u8; $bytes]) -> $t + { + $t($ti::from_le_bytes(b)) + } + + /// Create a value from its representation as a byte array in + /// native byte order. + #[inline] + pub fn from_ne_bytes(b: [u8; $bytes]) -> $t + { + $t($ti::from_ne_bytes(b)) + } /// Creates a value of this type with the bit pattern `bits`. #[inline] - pub const fn from_bits(bits: $UT) -> Self {$Type(bits as $IT)} + pub const fn from_bits(bits: $tu) -> $t {$t(bits as $ti)} /// Creates a value of this type with the integral portion `n`. #[inline] - #[allow(clippy::cast_lossless)] - pub const fn from_int(n: $LT) -> Self - { - $Type(n as ($IT) << Self::FRACBITS) - } + pub const fn from_int(n: $ti) -> $t {$t(n << $t::frac_bits())} /// Returns the raw bit pattern. #[inline] - pub fn to_bits(&self) -> $UT {self.0 as $UT} + pub fn to_bits(self) -> $tu {self.0 as $tu} /// 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))} + pub fn set_bits(&mut self, bits: $tu) {self.0 = bits as $ti} #[inline] - fn fx_div(x: $IT, y: $IT) -> $IT + const fn mul_i(x: $ti, y: $ti) -> $ti {x * y} + + #[inline] + const fn div_i(x: $ti, y: $ti) -> $ti {x / y} + + #[inline] + fn div_k(x: $ti, y: $ti) -> $ti { - (i64::from(x) * i64::from(Self::ONE) / i64::from(y)) as $IT + (i64::from(x) * i64::from($t::one()) / i64::from(y)) as $ti } #[inline] - fn fx_mul(x: $IT, y: $IT) -> $IT + fn mul_k(x: $ti, y: $ti) -> $ti { - (i64::from(x) * i64::from(y) / i64::from(Self::ONE)) as $IT + (i64::from(x) * i64::from(y) / i64::from($t::one())) as $ti } } - impl From<$LT> for $Type + impl From<$ti> for $t { #[inline] - fn from(n: $LT) -> Self {$Type::from_int(n)} + fn from(n: $ti) -> $t {$t::from_int(n)} } - impl ops::Add for $Type + impl Add<$t> for $t { - type Output = Self; + type Output = $t; #[inline] - fn add(self, o: Self) -> Self {$Type(self.0 + o.0)} + fn add(self, o: $t) -> $t {$t(self.0 + o.0)} } - impl ops::Sub for $Type + fixed_ref_binop! {impl Add, add for $t, $t} + + impl Sub<$t> for $t { - type Output = Self; + type Output = $t; #[inline] - fn sub(self, o: Self) -> Self {$Type(self.0 - o.0)} + fn sub(self, o: $t) -> $t {$t(self.0 - o.0)} } - impl ops::Mul for $Type + fixed_ref_binop! {impl Sub, sub for $t, $t} + + impl Mul<$t> for $t { - type Output = Self; + type Output = $t; #[inline] - fn mul(self, o: Self) -> Self {$Type(Self::fx_mul(self.0, o.0))} + fn mul(self, o: $t) -> $t {$t($t::mul_k(self.0, o.0))} } - impl ops::Div for $Type + fixed_ref_binop! {impl Mul, mul for $t, $t} + + impl Mul<$ti> for $t { - type Output = Self; + type Output = $t; #[inline] - fn div(self, o: Self) -> Self {$Type(Self::fx_div(self.0, o.0))} + fn mul(self, o: $ti) -> $t {$t($t::mul_i(self.0, o))} } - impl ops::Neg for $Type + fixed_ref_binop! {impl Mul, mul for $t, $ti} + + impl Div<$t> for $t { - type Output = Self; + type Output = $t; #[inline] - fn neg(self) -> Self {$Type(-self.0)} + fn div(self, o: $t) -> $t {$t($t::div_k(self.0, o.0))} } - impl ops::Not for $Type + fixed_ref_binop! {impl Div, div for $t, $t} + + impl Div<$ti> for $t { - type Output = Self; + type Output = $t; #[inline] - fn not(self) -> Self {$Type(!self.0)} + fn div(self, o: $ti) -> $t {$t($t::div_i(self.0, o))} } - impl fmt::Display for $Type + fixed_ref_binop! {impl Div, div for $t, $ti} + + impl Neg for $t + { + type Output = $t; + + #[inline] + fn neg(self) -> $t {$t(-self.0)} + } + + fixed_ref_unop! {impl Neg, neg for $t} + + impl Not for $t + { + type Output = $t; + + #[inline] + fn not(self) -> $t {$t(!self.0)} + } + + fixed_ref_unop! {impl Not, not for $t} + + impl AddAssign for $t + { + #[inline] + fn add_assign(&mut self, other: $t) {self.0 += other.0} + } + + fixed_ref_op_assign! {impl AddAssign, add_assign for $t, $t} + + impl SubAssign for $t + { + #[inline] + fn sub_assign(&mut self, other: $t) {self.0 -= other.0} + } + + fixed_ref_op_assign! {impl SubAssign, sub_assign for $t, $t} + + impl MulAssign for $t + { + #[inline] + fn mul_assign(&mut self, other: $t) {self.0 = (*self * other).0} + } + + fixed_ref_op_assign! {impl MulAssign, mul_assign for $t, $t} + + impl DivAssign for $t + { + #[inline] + fn div_assign(&mut self, other: $t) {self.0 = (*self / other).0} + } + + fixed_ref_op_assign! {impl DivAssign, div_assign for $t, $t} + + impl fmt::Display for $t { 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)?; + + write!(f, "{:widt$}.", self.0 >> $t::frac_bits(), widt = widt)?; let mut k = self.to_bits(); + for _ in 0..prec { - k &= Self::FRACMASK; + k &= $t::frac_mask(); k *= 10; - let d = k >> Self::FRACBITS; + + let d = k >> $t::frac_bits(); let d = d % 10; - f.write_char((d as u8 + b'0') as char)?; + + f.write_char(char::from(d as u8 + b'0'))?; } Ok(()) } } - impl fmt::Debug for $Type + impl fmt::Debug for $t { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(self, f) } } - }; + )*}; } -define_fixed_type! { +define_fixed_types! { /// 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; -} + struct Angle(i16, 2) : u16, 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; -} + struct Unit(i16, 2) : u16, 10; -define_fixed_type! { /// A generic fixed point type. /// /// The format of this type is `15.16s`. - struct Fixed(i32) : u32, i16, 16; + struct Fixed(i32, 4) : u32, 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); + let one = Fixed::one(); + let two = 2 << Fixed::frac_bits(); + let twelve = 12 << Fixed::frac_bits(); + + assert_eq!((Fixed::from(1) + Fixed::from(1)).to_bits(), two); + assert_eq!((Fixed::from(2) - Fixed::from(1)).to_bits(), one); + assert_eq!((Fixed::from(6) * Fixed::from(2)).to_bits(), twelve); + assert_eq!((Fixed::from(6) * 2) .to_bits(), twelve); +} + +#[test] +fn fixed_fractions() +{ + let three_pt_5 = 3 << Fixed::frac_bits() | Fixed::one() / 2; + let one_pt_2 = 1 << Fixed::frac_bits() | Fixed::one() / 5; + let two_pt_4 = 157_286; + + assert_eq!((Fixed::from(7) / Fixed::from(2)).to_bits(), three_pt_5); + assert_eq!((Fixed::from(7) / 2) .to_bits(), three_pt_5); + assert_eq!((Fixed::from_bits(one_pt_2) * 2) .to_bits(), two_pt_4); } #[test] #[should_panic] #[allow(unused_must_use)] -fn fixed_overflow() {Fixed::from(i16::max_value()) + 1.into();} +fn fixed_overflow() {Fixed::from(i16::max_value() as i32) + Fixed::from(1);} #[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!("{}", Fixed::from(6)), "6.0"); + assert_eq!(format!("{}", Fixed::from(7) / 2), "3.5"); assert_eq!(format!("{:7.7}", Fixed::from_bits(0xDEAD_BEEF)), " -8531.7458343"); }