implementation can be customized through macro
This commit is contained in:
parent
971906593d
commit
3295b67070
40
README.md
40
README.md
|
@ -1,3 +1,41 @@
|
|||
# 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
|
146
src/lib.rs
146
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<i32> 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<f32> 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<i32> 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<f32> 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<Fixed32> 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<Fixed32> 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<Fixed32> 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<Fixed32> 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;
|
||||
|
||||
|
|
Loading…
Reference in New Issue