fixed_point/
lib.rs

1//! # Fixed-point numbers
2//!
3//! fixed-point numbers use constant D as decimal digit length
4//! e.g. `fixed!(1.1i16, 2)` will declare number as `110i16`
5//!
6//! * Define a constant fixed-point number
7//!
8//!   ```
9//!   use fixed_point::{fixed, FixedPoint};
10//!   const FIXED_POINT: FixedPoint<u16, 3> = fixed!(0.25, 3);
11//!   ```
12//!
13//! * Define a fixed-point variable
14//!
15//!   ```
16//!   use fixed_point::fixed;
17//!   let decimal = fixed!(-1.1i16, 2);
18//!   ```
19//!
20//! * Define a implicit precision fixed-point variable
21//!
22//!   ```
23//!   use fixed_point::fixed;
24//!   let decimal = fixed!(-1.1i16);
25//!   ```
26
27#![cfg_attr(not(any(test, feature = "std")), no_std)]
28
29/// Define a fixed-point number
30pub use macros::fixed;
31
32use core::{convert, fmt::Display, ops, str::FromStr};
33#[cfg(all(feature = "serde", not(any(test, feature = "std"))))]
34use num_traits::float::FloatCore;
35use num_traits::pow::Pow;
36
37#[derive(Copy, Clone, Default, Debug, PartialEq, PartialOrd)]
38pub struct FixedPoint<T, const D: u8>(pub T);
39
40impl<T, const D: u8> FixedPoint<T, D> {
41    pub fn decimal_length(self) -> u8 {
42        D
43    }
44
45    pub fn exp(self) -> usize {
46        10_usize.pow(D as u32)
47    }
48}
49
50pub trait Number {
51    fn ten() -> Self;
52    fn zero() -> Self;
53}
54
55macro_rules! impl_number {
56    ($($types:ty),+) => {
57        $(
58            impl Number for $types {
59                fn ten() -> Self {
60                    10
61                }
62
63                fn zero() -> Self {
64                    0
65                }
66            }
67        )+
68    };
69}
70
71impl_number!(u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, usize, isize);
72
73impl<T, const D: u8> FixedPoint<T, D>
74where
75    T: Number + Pow<u8, Output = T> + ops::Mul<Output = T> + ops::Add<Output = T>,
76{
77    pub fn new(number: T, decimal: u8) -> Self {
78        Self(number * T::ten().pow(D - decimal))
79    }
80}
81
82impl<T, const D: u8> FixedPoint<T, D>
83where
84    T: Copy + Number + Pow<u32, Output = T> + ops::Div<Output = T> + ops::Rem<Output = T>,
85{
86    pub fn integer(&self) -> T {
87        self.0 / (T::ten()).pow(D as u32)
88    }
89
90    pub fn decimal(&self) -> T {
91        self.0 % (T::ten()).pow(D as u32)
92    }
93}
94
95impl<T: ops::Div<Output = T>, const D: u8> ops::Div<T> for FixedPoint<T, D> {
96    type Output = Self;
97
98    fn div(self, div: T) -> Self {
99        Self(self.0 / div)
100    }
101}
102
103impl<T: Copy + Into<i32>, const D: u8> Into<f32> for FixedPoint<T, D> {
104    fn into(self) -> f32 {
105        let value: i32 = self.0.into();
106        value as f32 / self.exp() as f32
107    }
108}
109
110impl<T: convert::TryFrom<isize>, const D: u8> FromStr for FixedPoint<T, D> {
111    type Err = ();
112
113    fn from_str(string: &str) -> Result<Self, Self::Err> {
114        let negative = string.chars().next().map(|c| c == '-').unwrap_or(false);
115        let mut splitted = string.split('.');
116        let mut integer = splitted
117            .next()
118            .ok_or(())?
119            .parse::<isize>()
120            .map_err(|_| ())?;
121        integer *= (10 as isize).pow(D as u32);
122        let field = match splitted.next() {
123            Some(s) => s,
124            None => return T::try_from(integer).map(|v| Self(v)).map_err(|_| ()),
125        };
126        let decimal_length = core::cmp::min(field.len(), 255) as u8;
127        let mut decimal = field.parse::<isize>().map_err(|_| ())?;
128        if integer < 0 || negative {
129            decimal = -decimal
130        }
131        if D >= decimal_length {
132            decimal *= (10 as isize).pow((D - decimal_length) as u32);
133        } else {
134            decimal /= (10 as isize).pow((decimal_length - D) as u32);
135        }
136        T::try_from(integer + decimal)
137            .map(|v| Self(v))
138            .map_err(|_| ())
139    }
140}
141
142impl<T, const D: u8> Display for FixedPoint<T, D>
143where
144    T: Copy
145        + Display
146        + Into<i32>
147        + PartialEq
148        + Number
149        + PartialOrd
150        + Pow<u32, Output = T>
151        + ops::Div<Output = T>
152        + ops::Rem<Output = T>,
153{
154    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
155        let mut decimal = self.decimal().into().abs();
156        if D == 0 || decimal == 0 {
157            return write!(f, "{}.0", self.integer());
158        }
159        let mut length = D;
160        while decimal % 10 == 0 {
161            decimal = decimal / 10;
162            length -= 1;
163        }
164        let integer = self.integer();
165        if integer == T::zero() && self.0 < T::zero() {
166            write!(f, "-0.{:0length$}", decimal, length = length as usize)
167        } else {
168            write!(
169                f,
170                "{}.{:0length$}",
171                integer,
172                decimal,
173                length = length as usize
174            )
175        }
176    }
177}
178
179#[cfg(feature = "serde")]
180impl<T: Copy + Into<i32>, const D: u8> serde::Serialize for FixedPoint<T, D> {
181    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
182        serializer.serialize_f32((*self).into())
183    }
184}
185
186#[cfg(feature = "serde")]
187impl<'a, T: convert::TryFrom<isize>, const D: u8> serde::Deserialize<'a> for FixedPoint<T, D> {
188    fn deserialize<DE: serde::Deserializer<'a>>(deserializer: DE) -> Result<Self, DE::Error> {
189        let float = <f32>::deserialize(deserializer)?;
190        let v = (float * 10f32.powi(D as i32)) as isize;
191        T::try_from(v)
192            .map(|v| Self(v))
193            .map_err(|_| <DE::Error as serde::de::Error>::custom("Not fixed-point"))
194    }
195}