From 3295b67070b5aad6524bf9cb03645dd2b45e106e Mon Sep 17 00:00:00 2001 From: teridax Date: Thu, 4 May 2023 18:20:14 +0200 Subject: [PATCH] implementation can be customized through macro --- README.md | 40 ++++++++++++++- src/lib.rs | 146 +++++++++++++++++++++++++++++++++-------------------- 2 files changed, 129 insertions(+), 57 deletions(-) diff --git a/README.md b/README.md index 5fa9439..7799f62 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,41 @@ # staticdot -Rust library for general purpose fixed point arithmetic \ No newline at end of file +Rust library for general purpose fixed point arithmetic. + +This library exposes the functionality to implement custom fixed point arithmetic +through the use of macros. + +The most important macro is `impl_fixed_point`. This macro requires a name for the new types name, a raw type which will store the actual fixed point data +and the number of bits to use for the fraction. +The raw type used to store the fixed point data should be a primitive type of either: +* usize +* isize +* u8 +* u16 +* u32 +* u64 +* i8 +* i16 +* i32 +* i64 + +Theoretically any type can be used if it implements the required traits. + +Quick start example:: +```rust +impl_fixed_point!(Fixed64_32, i64, 32); + +fn main() { + let a = Fixed64_32::ZERO; + let b = Fixed64_32::from(3); + + let c = a + b * Fixed64_32::from(4.5); + + println!("{}", Into::::into(c)); +} +``` + +## TODO +* add support for special functions such as sin(), sqrt(), ln() +* add macro for converting between different formats +* make precision retain shifts in operations customizable \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 8d9349f..a5adfe0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,84 +1,118 @@ -use std::ops; -use std::ops::{Add, BitAnd, Div, Mul, Shl, Shr, Sub}; -const INTEGER_BITS: i32 = 8; -const FRACTION_BITS: i32 = 24; -/// in oder to create the fraction bits mask we first fill all bits with 1 by initializing it with -1 -/// then we reinterpret the -1 as unsigned and shift it to the left. We need to do this in order to -/// avoid arithmetic shifts -const FRACTION_MASK: i32 = (-1i32 as u32 >> INTEGER_BITS) as i32; +macro_rules! __impl_from_into_for_rational { + ($impl_name:ident, $raw_type:ty, $frac_bits:expr, $from_type:ty) => { + impl From<$from_type> for $impl_name { + fn from(value: $from_type) -> Self { + let integral = (value as $raw_type).shl($frac_bits); + let fraction = (value.fract() * 1.shl($frac_bits) as $from_type) as $raw_type; + $impl_name(integral | fraction) + } + } -#[derive(Copy, Clone)] -pub struct Fixed32(i32); + impl Into<$from_type> for $impl_name { + fn into(self) -> $from_type { -impl From for Fixed32 { - fn from(value: i32) -> Self { - Fixed32(value.shl(FRACTION_BITS)) + let integral = self.0.shr($frac_bits); + let fraction = (self.0.bitand(FRAC_MASK)) as $from_type / (1 as $raw_type).shl($frac_bits) as $from_type; + + integral as $from_type + fraction + } + } } } -impl From for Fixed32 { - fn from(value: f32) -> Self { - let integral = (value as i32).shl(FRACTION_BITS); - let fraction = (value.fract() * 1.shl(FRACTION_BITS) as f32) as i32; - Fixed32(integral | fraction) - } -} +macro_rules! impl_fixed_point { + ($impl_name:ident, $raw_type:ty, $frac_bits:expr) => { + mod $impl_name { + use std::ops; + use std::ops::{Add, BitAnd, Div, Mul, Shl, Shr, Sub}; -impl Into for Fixed32 { - fn into(self) -> i32 { - self.0.shr(FRACTION_BITS) - } -} + #[derive(Copy, Clone, PartialOrd, PartialEq, Eq, Ord)] + pub struct $impl_name($raw_type); -impl Into for Fixed32 { - fn into(self) -> f32 { - let integral = self.0.shr(FRACTION_BITS) as f32; - let fraction = self.0.bitand(FRACTION_MASK) as f32 / 1.shl(FRACTION_BITS) as f32; - integral + fraction - } -} + pub const ZERO: $impl_name = $impl_name(0); -impl Add for Fixed32 { - type Output = Fixed32; + const FRAC_MASK: $raw_type = (-1) << (std::mem::size_of::<$raw_type>() * 8 - $frac_bits); - fn add(self, rhs: Fixed32) -> Self::Output { - Fixed32(self.0 + rhs.0) - } -} + impl From<$raw_type> for $impl_name { + fn from(value: $raw_type) -> Self { + $impl_name(value.shl($frac_bits)) + } + } -impl Sub for Fixed32 { - type Output = Fixed32; + __impl_from_into_for_rational!($impl_name, $raw_type, $frac_bits, f32); + __impl_from_into_for_rational!($impl_name, $raw_type, $frac_bits, f64); - fn sub(self, rhs: Fixed32) -> Self::Output { - Fixed32(self.0 - rhs.0) - } -} + impl Into<$raw_type> for $impl_name { + fn into(self) -> $raw_type { + self.0.shr($frac_bits) + } + } -impl Mul for Fixed32 { - type Output = Fixed32; + impl Add<$impl_name> for $impl_name { + type Output = $impl_name; - fn mul(self, rhs: Fixed32) -> Self::Output { - Fixed32(self.0.shr(FRACTION_BITS / 2) * rhs.0.shr(FRACTION_BITS / 2)) - } -} + fn add(self, rhs: $impl_name) -> Self::Output { + $impl_name(self.0 + rhs.0) + } + } -impl Div for Fixed32 { - type Output = Fixed32; + impl Sub<$impl_name> for $impl_name { + type Output = $impl_name; - fn div(self, rhs: Fixed32) -> Self::Output { - Fixed32((self.0 / rhs.0.shr(FRACTION_BITS / 2)).shl(FRACTION_BITS / 2)) + fn sub(self, rhs: $impl_name) -> Self::Output { + $impl_name(self.0 - rhs.0) + } + } + + impl Mul<$impl_name> for $impl_name { + type Output = $impl_name; + + fn mul(self, rhs: $impl_name) -> Self::Output { + $impl_name(self.0.shr($frac_bits / 2) * rhs.0.shr($frac_bits / 2)) + } + } + + impl Div<$impl_name> for $impl_name { + type Output = $impl_name; + + fn div(self, rhs: $impl_name) -> Self::Output { + $impl_name((self.0 / rhs.0.shr($frac_bits / 2 as $raw_type)).shl($frac_bits / 2)) + } + } + + impl Default for $impl_name { + fn default() -> Self { + ZERO + } + } + + impl $impl_name { + + pub fn integral(&self) -> $raw_type { + self.0.shr($frac_bits) + } + + pub fn fraction_bits(&self) -> $raw_type { + self.0 + } + } + } } } #[cfg(test)] mod tests { + use crate::tests::LowpFixed::LowpFixed; use super::*; + impl_fixed_point!(LowpFixed, i32, 16); + impl_fixed_point!(HighpFixed, i64, 32); + #[test] fn test() { - let a = Fixed32::from(9); - let b = Fixed32::from(3); + let a = HighpFixed::ZERO; + let b = HighpFixed::HighpFixed::from(3); let c = a * a / b;