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
|
# 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
|
144
src/lib.rs
144
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;
|
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};
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialOrd, PartialEq, Eq, Ord)]
|
||||||
|
pub struct $impl_name($raw_type);
|
||||||
|
|
||||||
|
pub const ZERO: $impl_name = $impl_name(0);
|
||||||
|
|
||||||
|
const FRAC_MASK: $raw_type = (-1) << (std::mem::size_of::<$raw_type>() * 8 - $frac_bits);
|
||||||
|
|
||||||
|
impl From<$raw_type> for $impl_name {
|
||||||
|
fn from(value: $raw_type) -> Self {
|
||||||
|
$impl_name(value.shl($frac_bits))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl Into<i32> for Fixed32 {
|
|
||||||
fn into(self) -> i32 {
|
|
||||||
self.0.shr(FRACTION_BITS)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl Into<f32> for Fixed32 {
|
__impl_from_into_for_rational!($impl_name, $raw_type, $frac_bits, f32);
|
||||||
fn into(self) -> f32 {
|
__impl_from_into_for_rational!($impl_name, $raw_type, $frac_bits, f64);
|
||||||
let integral = self.0.shr(FRACTION_BITS) as f32;
|
|
||||||
let fraction = self.0.bitand(FRACTION_MASK) as f32 / 1.shl(FRACTION_BITS) as f32;
|
impl Into<$raw_type> for $impl_name {
|
||||||
integral + fraction
|
fn into(self) -> $raw_type {
|
||||||
|
self.0.shr($frac_bits)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl Add<Fixed32> for Fixed32 {
|
|
||||||
type Output = Fixed32;
|
|
||||||
|
|
||||||
fn add(self, rhs: Fixed32) -> Self::Output {
|
|
||||||
Fixed32(self.0 + rhs.0)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl Sub<Fixed32> for Fixed32 {
|
impl Add<$impl_name> for $impl_name {
|
||||||
type Output = Fixed32;
|
type Output = $impl_name;
|
||||||
|
|
||||||
fn sub(self, rhs: Fixed32) -> Self::Output {
|
fn add(self, rhs: $impl_name) -> Self::Output {
|
||||||
Fixed32(self.0 - rhs.0)
|
$impl_name(self.0 + rhs.0)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl Mul<Fixed32> for Fixed32 {
|
|
||||||
type Output = Fixed32;
|
|
||||||
|
|
||||||
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 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;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue