Maraiah/src/durandal/fixed.rs

201 lines
4.4 KiB
Rust

//! Fixed point numbers.
use serde::Serialize;
use std::{fmt::{self, Write},
ops};
macro_rules! define_fixed_type {
(
$Type:ident : $IT:ident, $UT:ident, $LT:ident, $FracBits:expr
) => {
#[derive(Clone, PartialEq, Serialize)]
pub struct $Type($IT);
impl $Type
{
const FRACBITS: $UT = $FracBits;
const FRACMASK: $UT = (1 << Self::FRACBITS) - 1;
const ONE: $IT = 1 << Self::FRACBITS;
pub fn to_bits(&self) -> $UT
{
self.0 as $UT
}
pub fn set_bits(&mut self, bits: $UT)
{
self.0 = bits as $IT
}
pub const fn from_bits(bits: $UT) -> Self
{
$Type(bits as $IT)
}
pub fn integ(&self) -> $LT
{
(self.0 >> Self::FRACBITS) as $LT
}
pub fn fract(&self) -> u16
{
(self.0 as $UT & Self::FRACMASK) as u16
}
pub fn mul_i(&self, n: $LT) -> Self
{
$Type(self.0 * $IT::from(n))
}
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
{
fn from(n: $LT) -> Self
{
$Type($IT::from(n) << Self::FRACBITS)
}
}
impl ops::Add for $Type
{
type Output = Self;
fn add(self, o: Self) -> Self
{
$Type(self.0 + o.0)
}
}
impl ops::Sub for $Type
{
type Output = Self;
fn sub(self, o: Self) -> Self
{
$Type(self.0 - o.0)
}
}
impl ops::Mul for $Type
{
type Output = Self;
fn mul(self, o: Self) -> Self
{
$Type(Self::fx_mul(self.0, o.0))
}
}
impl ops::Div for $Type
{
type Output = Self;
fn div(self, o: Self) -> Self
{
$Type(Self::fx_div(self.0, o.0))
}
}
impl ops::Neg for $Type
{
type Output = Self;
fn neg(self) -> Self
{
$Type(-self.0)
}
}
impl ops::Not for $Type
{
type Output = Self;
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!(Fixed: i32, u32, i16, 16);
define_fixed_type!(Unit: i16, u16, i8, 10);
define_fixed_type!(Angle: i16, u16, i8, 9);
#[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