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 @@
use std::ops;
use std::ops::{Add, BitAnd, Div, Mul, Shl, Shr, Sub};
const INTEGER_BITS: i32 = 8; macro_rules! __impl_from_into_for_rational {
const FRACTION_BITS: i32 = 24; ($impl_name:ident, $raw_type:ty, $frac_bits:expr, $from_type:ty) => {
/// in oder to create the fraction bits mask we first fill all bits with 1 by initializing it with -1 impl From<$from_type> for $impl_name {
/// then we reinterpret the -1 as unsigned and shift it to the left. We need to do this in order to fn from(value: $from_type) -> Self {
/// avoid arithmetic shifts let integral = (value as $raw_type).shl($frac_bits);
const FRACTION_MASK: i32 = (-1i32 as u32 >> INTEGER_BITS) as i32; let fraction = (value.fract() * 1.shl($frac_bits) as $from_type) as $raw_type;
$impl_name(integral | fraction)
}
}
#[derive(Copy, Clone)] impl Into<$from_type> for $impl_name {
pub struct Fixed32(i32); fn into(self) -> $from_type {
impl From<i32> for Fixed32 { let integral = self.0.shr($frac_bits);
fn from(value: i32) -> Self { let fraction = (self.0.bitand(FRAC_MASK)) as $from_type / (1 as $raw_type).shl($frac_bits) as $from_type;
Fixed32(value.shl(FRACTION_BITS))
integral as $from_type + fraction
}
}
} }
} }
impl From<f32> for Fixed32 { macro_rules! impl_fixed_point {
fn from(value: f32) -> Self { ($impl_name:ident, $raw_type:ty, $frac_bits:expr) => {
let integral = (value as i32).shl(FRACTION_BITS); mod $impl_name {
let fraction = (value.fract() * 1.shl(FRACTION_BITS) as f32) as i32; use std::ops;
Fixed32(integral | fraction) use std::ops::{Add, BitAnd, Div, Mul, Shl, Shr, Sub};
}
}
impl Into<i32> for Fixed32 { #[derive(Copy, Clone, PartialOrd, PartialEq, Eq, Ord)]
fn into(self) -> i32 { pub struct $impl_name($raw_type);
self.0.shr(FRACTION_BITS)
}
}
impl Into<f32> for Fixed32 { pub const ZERO: $impl_name = $impl_name(0);
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
}
}
impl Add<Fixed32> for Fixed32 { const FRAC_MASK: $raw_type = (-1) << (std::mem::size_of::<$raw_type>() * 8 - $frac_bits);
type Output = Fixed32;
fn add(self, rhs: Fixed32) -> Self::Output { impl From<$raw_type> for $impl_name {
Fixed32(self.0 + rhs.0) fn from(value: $raw_type) -> Self {
} $impl_name(value.shl($frac_bits))
} }
}
impl Sub<Fixed32> for Fixed32 { __impl_from_into_for_rational!($impl_name, $raw_type, $frac_bits, f32);
type Output = Fixed32; __impl_from_into_for_rational!($impl_name, $raw_type, $frac_bits, f64);
fn sub(self, rhs: Fixed32) -> Self::Output { impl Into<$raw_type> for $impl_name {
Fixed32(self.0 - rhs.0) fn into(self) -> $raw_type {
} self.0.shr($frac_bits)
} }
}
impl Mul<Fixed32> for Fixed32 { impl Add<$impl_name> for $impl_name {
type Output = Fixed32; type Output = $impl_name;
fn mul(self, rhs: Fixed32) -> Self::Output { fn add(self, rhs: $impl_name) -> Self::Output {
Fixed32(self.0.shr(FRACTION_BITS / 2) * rhs.0.shr(FRACTION_BITS / 2)) $impl_name(self.0 + rhs.0)
} }
} }
impl Div<Fixed32> for Fixed32 { impl Sub<$impl_name> for $impl_name {
type Output = Fixed32; type Output = $impl_name;
fn div(self, rhs: Fixed32) -> Self::Output { fn sub(self, rhs: $impl_name) -> Self::Output {
Fixed32((self.0 / rhs.0.shr(FRACTION_BITS / 2)).shl(FRACTION_BITS / 2)) $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)] #[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;