implementation can be customized through macro

This commit is contained in:
Sven Vogel 2023-05-04 18:20:14 +02:00
parent 971906593d
commit 3295b67070
2 changed files with 129 additions and 57 deletions

View File

@ -1,3 +1,41 @@
# staticdot # staticdot
Rust library for general purpose fixed point arithmetic 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::<f32>::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

View File

@ -1,84 +1,118 @@
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)
}
}
impl Into<$from_type> for $impl_name {
fn into(self) -> $from_type {
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
}
}
}
}
macro_rules! impl_fixed_point {
($impl_name:ident, $raw_type:ty, $frac_bits:expr) => {
mod $impl_name {
use std::ops; use std::ops;
use std::ops::{Add, BitAnd, Div, Mul, Shl, Shr, Sub}; use std::ops::{Add, BitAnd, Div, Mul, Shl, Shr, Sub};
const INTEGER_BITS: i32 = 8; #[derive(Copy, Clone, PartialOrd, PartialEq, Eq, Ord)]
const FRACTION_BITS: i32 = 24; pub struct $impl_name($raw_type);
/// 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;
#[derive(Copy, Clone)] pub const ZERO: $impl_name = $impl_name(0);
pub struct Fixed32(i32);
impl From<i32> for Fixed32 { const FRAC_MASK: $raw_type = (-1) << (std::mem::size_of::<$raw_type>() * 8 - $frac_bits);
fn from(value: i32) -> Self {
Fixed32(value.shl(FRACTION_BITS)) impl From<$raw_type> for $impl_name {
fn from(value: $raw_type) -> Self {
$impl_name(value.shl($frac_bits))
} }
} }
impl From<f32> for Fixed32 { __impl_from_into_for_rational!($impl_name, $raw_type, $frac_bits, f32);
fn from(value: f32) -> Self { __impl_from_into_for_rational!($impl_name, $raw_type, $frac_bits, f64);
let integral = (value as i32).shl(FRACTION_BITS);
let fraction = (value.fract() * 1.shl(FRACTION_BITS) as f32) as i32; impl Into<$raw_type> for $impl_name {
Fixed32(integral | fraction) fn into(self) -> $raw_type {
self.0.shr($frac_bits)
} }
} }
impl Into<i32> for Fixed32 { impl Add<$impl_name> for $impl_name {
fn into(self) -> i32 { type Output = $impl_name;
self.0.shr(FRACTION_BITS)
fn add(self, rhs: $impl_name) -> Self::Output {
$impl_name(self.0 + rhs.0)
} }
} }
impl Into<f32> for Fixed32 { impl Sub<$impl_name> for $impl_name {
fn into(self) -> f32 { type Output = $impl_name;
let integral = self.0.shr(FRACTION_BITS) as f32;
let fraction = self.0.bitand(FRACTION_MASK) as f32 / 1.shl(FRACTION_BITS) as f32; fn sub(self, rhs: $impl_name) -> Self::Output {
integral + fraction $impl_name(self.0 - rhs.0)
} }
} }
impl Add<Fixed32> for Fixed32 { impl Mul<$impl_name> for $impl_name {
type Output = Fixed32; type Output = $impl_name;
fn add(self, rhs: Fixed32) -> Self::Output { fn mul(self, rhs: $impl_name) -> Self::Output {
Fixed32(self.0 + rhs.0) $impl_name(self.0.shr($frac_bits / 2) * rhs.0.shr($frac_bits / 2))
} }
} }
impl Sub<Fixed32> for Fixed32 { impl Div<$impl_name> for $impl_name {
type Output = Fixed32; type Output = $impl_name;
fn sub(self, rhs: Fixed32) -> Self::Output { fn div(self, rhs: $impl_name) -> Self::Output {
Fixed32(self.0 - rhs.0) $impl_name((self.0 / rhs.0.shr($frac_bits / 2 as $raw_type)).shl($frac_bits / 2))
} }
} }
impl Mul<Fixed32> for Fixed32 { impl Default for $impl_name {
type Output = Fixed32; fn default() -> Self {
ZERO
fn mul(self, rhs: Fixed32) -> Self::Output {
Fixed32(self.0.shr(FRACTION_BITS / 2) * rhs.0.shr(FRACTION_BITS / 2))
} }
} }
impl Div<Fixed32> for Fixed32 { impl $impl_name {
type Output = Fixed32;
fn div(self, rhs: Fixed32) -> Self::Output { pub fn integral(&self) -> $raw_type {
Fixed32((self.0 / rhs.0.shr(FRACTION_BITS / 2)).shl(FRACTION_BITS / 2)) self.0.shr($frac_bits)
}
pub fn fraction_bits(&self) -> $raw_type {
self.0
}
}
}
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::tests::LowpFixed::LowpFixed;
use super::*; use super::*;
impl_fixed_point!(LowpFixed, i32, 16);
impl_fixed_point!(HighpFixed, i64, 32);
#[test] #[test]
fn test() { fn test() {
let a = Fixed32::from(9); let a = HighpFixed::ZERO;
let b = Fixed32::from(3); let b = HighpFixed::HighpFixed::from(3);
let c = a * a / b; let c = a * a / b;