use std::ops::*;
macro_rules! define_fixed {
(
$type_name:ident, $underlying_type:path, $intermediate_type:path, $frac_bits:literal,
from_int($($from_type:path),*),
into_float($($into_type:path),*)
) => {
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct $type_name($underlying_type);
impl $type_name {
pub const INTEGER_BITS: u8 =
(std::mem::size_of::<$underlying_type>() as u8) * 8 - $frac_bits;
pub const FRACTIONAL_BITS: u8 = $frac_bits;
pub const ZERO: Self = Self(0);
pub const ONE: Self = Self(1 << Self::FRACTIONAL_BITS);
pub const MIN: Self = Self(<$underlying_type>::MIN);
pub const MAX: Self = Self(<$underlying_type>::MAX);
#[inline]
pub const fn from_bits(n: $underlying_type) -> Self {
Self(n)
}
#[inline]
pub const fn get(self) -> $underlying_type {
self.0
}
#[inline]
pub fn from_f32(n: f32) -> Self {
Self((n * (1 << Self::FRACTIONAL_BITS) as f32) as $underlying_type)
}
#[inline]
pub fn from_f64(n: f64) -> Self {
Self((n * (1 << Self::FRACTIONAL_BITS) as f64) as $underlying_type)
}
#[inline]
pub fn to_f32(self) -> f32 {
self.0 as f32 / (1 << Self::FRACTIONAL_BITS) as f32
}
#[inline]
pub fn to_f64(self) -> f64 {
self.0 as f64 / (1 << Self::FRACTIONAL_BITS) as f64
}
#[inline]
pub const fn is_zero(self) -> bool {
self.0 == 0
}
#[inline]
pub const fn is_one(self) -> bool {
self.0 == 1 << Self::FRACTIONAL_BITS
}
#[inline]
pub fn mul_int(self, other: $underlying_type) -> $underlying_type {
let n = (<$intermediate_type>::from(self.0) * <$intermediate_type>::from(other))
>> Self::FRACTIONAL_BITS;
if cfg!(debug_assertions) {
<$underlying_type>::try_from(n).expect("Attempted to multiply with overflow")
} else {
n as $underlying_type
}
}
#[inline]
#[must_use]
pub const fn wrapping_neg(self) -> Self {
Self(self.0.wrapping_neg())
}
#[inline]
#[must_use]
pub const fn wrapping_add(self, other: Self) -> Self {
Self(self.0.wrapping_add(other.0))
}
#[inline]
#[must_use]
pub const fn wrapping_sub(self, other: Self) -> Self {
Self(self.0.wrapping_sub(other.0))
}
#[inline]
#[must_use]
pub const fn wrapping_mul(self, other: Self) -> Self {
let n = (self.0 as $intermediate_type)
.wrapping_mul(other.0 as $intermediate_type)
>> Self::FRACTIONAL_BITS;
Self(n as $underlying_type)
}
#[inline]
#[must_use]
pub const fn wrapping_div(self, other: Self) -> Self {
let n = ((self.0 as $intermediate_type) << Self::FRACTIONAL_BITS)
.wrapping_div(other.0 as $intermediate_type);
Self(n as $underlying_type)
}
#[inline]
pub const fn wrapping_mul_int(self, other: $underlying_type) -> $underlying_type {
let n = (self.0 as $intermediate_type)
.wrapping_mul(other as $intermediate_type)
>> Self::FRACTIONAL_BITS;
n as $underlying_type
}
}
impl Default for $type_name {
#[inline]
fn default() -> Self {
Self(0)
}
}
impl std::fmt::Display for $type_name {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.to_f64())
}
}
impl Neg for $type_name {
type Output = Self;
#[inline]
fn neg(self) -> Self {
Self(-self.0)
}
}
impl Add for $type_name {
type Output = Self;
#[inline]
fn add(self, other: Self) -> Self {
Self(self.0 + other.0)
}
}
impl AddAssign for $type_name {
#[inline]
fn add_assign(&mut self, other: Self) {
*self = *self + other
}
}
impl Sub for $type_name {
type Output = Self;
#[inline]
fn sub(self, other: Self) -> Self {
Self(self.0 - other.0)
}
}
impl SubAssign for $type_name {
#[inline]
fn sub_assign(&mut self, other: Self) {
*self = *self - other
}
}
impl Mul for $type_name {
type Output = Self;
#[inline]
fn mul(self, other: Self) -> Self {
let n = <$intermediate_type>::from(self.0) * <$intermediate_type>::from(other.0)
>> Self::FRACTIONAL_BITS;
if cfg!(debug_assertions) {
Self(<$underlying_type>::try_from(n).expect("Attempted to multiply with overflow"))
} else {
Self(n as $underlying_type)
}
}
}
impl MulAssign for $type_name {
#[inline]
fn mul_assign(&mut self, other: Self) {
*self = *self * other
}
}
impl Mul<$underlying_type> for $type_name {
type Output = Self;
#[inline]
fn mul(self, other: $underlying_type) -> Self {
Self(self.0 * other)
}
}
impl MulAssign<$underlying_type> for $type_name {
#[inline]
fn mul_assign(&mut self, other: $underlying_type) {
*self = *self * other
}
}
impl Mul<$type_name> for $underlying_type {
type Output = $type_name;
#[inline]
fn mul(self, other: $type_name) -> $type_name {
other * self
}
}
impl Div for $type_name {
type Output = Self;
#[inline]
fn div(self, other: Self) -> Self {
let n = ((<$intermediate_type>::from(self.0) << Self::FRACTIONAL_BITS)
/ <$intermediate_type>::from(other.0));
if cfg!(debug_assertions) {
Self(<$underlying_type>::try_from(n).expect("Attempted to divide with overflow"))
} else {
Self(n as $underlying_type)
}
}
}
impl DivAssign for $type_name {
#[inline]
fn div_assign(&mut self, other: Self) {
*self = *self / other
}
}
impl Div<$underlying_type> for $type_name {
type Output = Self;
#[inline]
fn div(self, other: $underlying_type) -> Self {
Self(self.0 / other)
}
}
impl DivAssign<$underlying_type> for $type_name {
#[inline]
fn div_assign(&mut self, other: $underlying_type) {
*self = *self / other
}
}
$(
impl From<$from_type> for $type_name {
#[inline]
fn from(n: $from_type) -> Self {
Self(<$underlying_type>::from(n) << <$underlying_type>::from(Self::FRACTIONAL_BITS))
}
}
)*
$(
impl From<$type_name> for $into_type {
#[inline]
fn from(n: $type_name) -> $into_type {
n.0 as $into_type / (1 << <$type_name>::FRACTIONAL_BITS) as $into_type
}
}
)*
};
}
define_fixed!(Fixed8, i16, i32, 8, from_int(i8), into_float(f32, f64));
define_fixed!(Fixed16, i32, i64, 16, from_int(i8, i16), into_float(f64));
#[cfg(test)]
pub mod tests {
use super::*;
#[test]
fn from_int() {
assert_eq!(Fixed8::from(0).get(), 0x0);
assert_eq!(Fixed8::from(1).get(), 0x01_00);
assert_eq!(Fixed8::from(-1).get(), 0xff_00_u16 as i16);
assert_eq!(Fixed8::from(54).get(), 0x36_00);
assert_eq!(Fixed8::from(-84).get(), 0xac_00_u16 as i16);
assert_eq!(Fixed8::from(127).get(), 0x7f_00);
assert_eq!(Fixed8::from(-128).get(), 0x80_00_u16 as i16);
}
#[test]
fn from_float() {
assert_eq!(Fixed8::from_f64(0.0).get(), 0x0);
assert_eq!(Fixed8::from_f64(0.5).get(), 0x00_80);
assert_eq!(Fixed8::from_f64(-12.01171875).get(), 0xf3_fd_u16 as i16);
assert_eq!(Fixed8::from_f64(127.99609375).get(), 0x7f_ff_u16 as i16);
assert_eq!(Fixed8::from_f64(-0.00390625).get(), 0xff_ff_u16 as i16);
assert_eq!(Fixed8::from_f64(-128.0).get(), 0x80_00_u16 as i16);
assert_eq!(
Fixed8::from_f64(f64::NEG_INFINITY).get(),
0x80_00_u16 as i16
);
assert_eq!(Fixed8::from_f64(f64::INFINITY).get(), 0x7f_ff);
assert_eq!(Fixed8::from_f64(f64::NAN).get(), 0x00_00);
assert_eq!(Fixed8::from_f64(0.002).get(), 0x0);
assert_eq!(Fixed8::from_f64(64.004).get(), 0x40_01);
assert_eq!(Fixed8::from_f64(-64.004).get(), 0xbf_ff_u16 as i16);
}
#[test]
fn to_float() {
assert_eq!(Fixed8::from_bits(0x0).to_f64(), 0.0);
assert_eq!(Fixed8::from_bits(0x01_00).to_f64(), 1.0);
assert_eq!(Fixed8::from_bits(0x00_80).to_f64(), 0.5);
assert_eq!(Fixed8::from_bits(0xf3_fd_u16 as i16).to_f64(), -12.01171875);
assert_eq!(Fixed8::from_bits(0x7f_ff).to_f64(), 127.99609375);
assert_eq!(Fixed8::from_bits(0xff_ff_u16 as i16).to_f64(), -0.00390625);
assert_eq!(Fixed8::from_bits(0x80_00_u16 as i16).to_f64(), -128.0);
}
#[test]
fn display() {
assert_eq!(Fixed8::ZERO.to_string(), "0");
assert_eq!(Fixed8::ONE.to_string(), "1");
assert_eq!(Fixed8::from(5).to_string(), "5");
assert_eq!(Fixed8::from_f64(-3.5).to_string(), "-3.5");
assert_eq!(Fixed8::from_f64(127.99609375).to_string(), "127.99609375");
}
#[test]
fn neg() {
assert_eq!(-Fixed8::from(0), Fixed8::from(0));
assert_eq!(-Fixed8::from(1), Fixed8::from(-1));
assert_eq!(-Fixed8::from(-2), Fixed8::from(2));
assert_eq!(-Fixed8::from_f64(33.5), Fixed8::from_f64(-33.5));
assert_eq!(
-Fixed8::from_f64(127.99609375),
Fixed8::from_f64(-127.99609375)
);
}
#[test]
fn wrapping_neg() {
assert_eq!(Fixed8::from(-128).wrapping_neg(), Fixed8::from(-128));
}
#[test]
#[cfg_attr(debug_assertions, should_panic)]
fn neg_overflow() {
let _ = -Fixed8::from(-128);
}
#[test]
fn add() {
assert_eq!(Fixed8::ZERO + Fixed8::ZERO, Fixed8::ZERO);
assert_eq!(Fixed8::ZERO + Fixed8::ONE, Fixed8::ONE);
assert_eq!(Fixed8::ONE + Fixed8::ZERO, Fixed8::ONE);
assert_eq!(Fixed8::from(7) + Fixed8::from(5), Fixed8::from(12));
assert_eq!(
Fixed8::from_f64(1.75) + Fixed8::from_f64(7.25),
Fixed8::from_f64(9.0),
);
assert_eq!(
Fixed8::from_f64(123.5) + Fixed8::from_f64(-1.0),
Fixed8::from_f64(122.5),
);
assert_eq!(
Fixed8::from_f64(-64.5) + Fixed8::from_f64(-5.125),
Fixed8::from_f64(-69.625),
);
let mut n = Fixed8::from_f64(126.0);
n += Fixed8::from_f64(1.5);
assert_eq!(n, Fixed8::from_f64(127.5))
}
#[test]
fn add_wrapped() {
assert_eq!(
Fixed8::from(-128).wrapping_add(Fixed8::from(-1)),
Fixed8::from(127)
);
assert_eq!(
Fixed8::from(127).wrapping_add(Fixed8::from(1)),
Fixed8::from(-128)
);
}
#[test]
#[cfg_attr(debug_assertions, should_panic)]
fn add_overflow() {
let _ = Fixed8::from(-128) + Fixed8::from(-1);
}
#[test]
fn sub() {
assert_eq!(Fixed8::ZERO - Fixed8::ZERO, Fixed8::ZERO);
assert_eq!(Fixed8::ONE - Fixed8::ZERO, Fixed8::ONE);
assert_eq!(Fixed8::ZERO - Fixed8::ONE, Fixed8::from(-1));
assert_eq!(Fixed8::from(7) - Fixed8::from(5), Fixed8::from(2));
assert_eq!(
Fixed8::from_f64(1.75) - Fixed8::from_f64(7.25),
Fixed8::from_f64(-5.5),
);
assert_eq!(
Fixed8::from_f64(123.5) - Fixed8::from_f64(-1.0),
Fixed8::from_f64(124.5),
);
assert_eq!(
Fixed8::from_f64(-64.5) - Fixed8::from_f64(-5.125),
Fixed8::from_f64(-59.375),
);
let mut n = Fixed8::from_f64(126.0);
n -= Fixed8::from_f64(1.5);
assert_eq!(n, Fixed8::from_f64(124.5))
}
#[test]
fn sub_wrapped() {
assert_eq!(
Fixed8::from(-128).wrapping_sub(Fixed8::from(1)),
Fixed8::from(127)
);
assert_eq!(
Fixed8::from(127).wrapping_sub(Fixed8::from(-1)),
Fixed8::from(-128)
);
}
#[test]
#[cfg_attr(debug_assertions, should_panic)]
fn sub_overflow() {
let _ = Fixed8::from(-128) - Fixed8::from(1);
}
#[test]
fn mul() {
assert_eq!(Fixed8::ZERO * Fixed8::ZERO, Fixed8::ZERO);
assert_eq!(Fixed8::ONE * Fixed8::ZERO, Fixed8::ZERO);
assert_eq!(Fixed8::from(7) * Fixed8::from(5), Fixed8::from(35));
assert_eq!(Fixed8::from(5) * Fixed8::from(7), Fixed8::from(35));
assert_eq!(
Fixed8::from_f64(1.75) * Fixed8::from_f64(7.25),
Fixed8::from_f64(12.6875),
);
assert_eq!(
Fixed8::from_f64(-1.75) * Fixed8::from_f64(7.25),
Fixed8::from_f64(-12.6875),
);
assert_eq!(
Fixed8::from_f64(-5.03125) * Fixed8::from_f64(-5.03125),
Fixed8::from_f64(25.3125),
);
assert_eq!(
Fixed8::from_f64(5.03125) * Fixed8::from_f64(-5.03125),
Fixed8::from_f64(-25.31640625),
);
let mut n = Fixed8::from_f64(126.0);
n -= Fixed8::from_f64(1.5);
assert_eq!(n, Fixed8::from_f64(124.5))
}
#[test]
fn mul_wrapped() {
assert_eq!(
Fixed8::from(-128).wrapping_sub(Fixed8::from(1)),
Fixed8::from(127)
);
assert_eq!(
Fixed8::from(127).wrapping_sub(Fixed8::from(-1)),
Fixed8::from(-128)
);
}
#[test]
#[cfg_attr(debug_assertions, should_panic)]
fn mul_overflow() {
let _ = Fixed8::from(64) * Fixed8::from(64);
}
#[test]
fn mul_int() {
assert_eq!(Fixed8::from_f64(1.5).mul_int(2), 3);
assert_eq!(Fixed8::from_f64(-1.5).mul_int(2), -3);
assert_eq!(Fixed8::from_f64(1.5).mul_int(-2), -3);
assert_eq!(Fixed8::from_f64(-1.5).mul_int(-2), 3);
assert_eq!(Fixed8::from_f64(5.5).mul_int(126), 693);
assert_eq!(Fixed8::from_f64(3.75).mul_int(101), 378);
assert_eq!(Fixed8::from_f64(-3.75).mul_int(101), -379);
}
#[test]
fn mul_int_wrapped() {
assert_eq!(Fixed8::from_f64(127.5).wrapping_mul_int(30001), 24039);
assert_eq!(Fixed8::from_f64(-127.5).wrapping_mul_int(30001), -24040);
}
#[test]
#[cfg_attr(debug_assertions, should_panic)]
fn mul_int_overflow() {
let _ = Fixed8::from_f64(127.5).mul_int(30001);
}
#[test]
fn div() {
assert_eq!(Fixed8::ZERO / Fixed8::ONE, Fixed8::ZERO);
assert_eq!(Fixed8::from(4) / Fixed8::from(2), Fixed8::from(2));
assert_eq!(Fixed8::from(2) / Fixed8::from(4), Fixed8::from_f64(0.5));
assert_eq!(
Fixed8::from_f64(7.5) / Fixed8::from_f64(0.5),
Fixed8::from_f64(15.0),
);
assert_eq!(
Fixed8::from_f64(68.75) / Fixed8::from_f64(-12.5),
Fixed8::from_f64(-5.5),
);
assert_eq!(
Fixed8::from_f64(-84.25) / Fixed8::from_f64(-5.03125),
Fixed8::from_f64(16.7421875),
);
assert_eq!(
Fixed8::from_f64(84.25) / Fixed8::from_f64(-5.03125),
Fixed8::from_f64(-16.7421875),
);
let mut n = Fixed8::from_f64(126.0);
n /= Fixed8::from_f64(1.5);
assert_eq!(n, Fixed8::from_f64(84.0))
}
#[test]
fn div_wrapped() {
assert_eq!(
Fixed8::from(-128).wrapping_div(Fixed8::from(-1)),
Fixed8::from(-128)
);
assert_eq!(
Fixed8::from(127).wrapping_div(Fixed8::from_f64(0.5)),
Fixed8::from(-2)
);
}
#[test]
#[cfg_attr(debug_assertions, should_panic)]
fn div_overflow() {
let _ = Fixed8::from(-128) / Fixed8::from(-1);
}
#[test]
#[should_panic]
fn div_by_zero() {
let _ = Fixed8::ONE / Fixed8::ZERO;
}
}