#![cfg_attr(not(any(test, feature = "std")), no_std)]
pub use macros::fixed;
use core::{convert, fmt::Display, ops, str::FromStr};
#[cfg(all(feature = "serde", not(any(test, feature = "std"))))]
use num_traits::float::FloatCore;
use num_traits::pow::Pow;
#[derive(Copy, Clone, Default, Debug, PartialEq, PartialOrd)]
pub struct FixedPoint<T, const D: u8>(pub T);
impl<T, const D: u8> FixedPoint<T, D> {
pub fn decimal_length(self) -> u8 {
D
}
pub fn exp(self) -> usize {
10_usize.pow(D as u32)
}
}
pub trait Number {
fn ten() -> Self;
fn zero() -> Self;
}
macro_rules! impl_number {
($($types:ty),+) => {
$(
impl Number for $types {
fn ten() -> Self {
10
}
fn zero() -> Self {
0
}
}
)+
};
}
impl_number!(u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, usize, isize);
impl<T, const D: u8> FixedPoint<T, D>
where
T: Number + Pow<u8, Output = T> + ops::Mul<Output = T> + ops::Add<Output = T>,
{
pub fn new(number: T, decimal: u8) -> Self {
Self(number * T::ten().pow(D - decimal))
}
}
impl<T, const D: u8> FixedPoint<T, D>
where
T: Copy + Number + Pow<u32, Output = T> + ops::Div<Output = T> + ops::Rem<Output = T>,
{
pub fn integer(&self) -> T {
self.0 / (T::ten()).pow(D as u32)
}
pub fn decimal(&self) -> T {
self.0 % (T::ten()).pow(D as u32)
}
}
impl<T: ops::Div<Output = T>, const D: u8> ops::Div<T> for FixedPoint<T, D> {
type Output = Self;
fn div(self, div: T) -> Self {
Self(self.0 / div)
}
}
impl<T: Copy + Into<i32>, const D: u8> Into<f32> for FixedPoint<T, D> {
fn into(self) -> f32 {
let value: i32 = self.0.into();
value as f32 / self.exp() as f32
}
}
impl<T: convert::TryFrom<isize>, const D: u8> FromStr for FixedPoint<T, D> {
type Err = ();
fn from_str(string: &str) -> Result<Self, Self::Err> {
let negative = string.chars().next().map(|c| c == '-').unwrap_or(false);
let mut splitted = string.split('.');
let mut integer = splitted
.next()
.ok_or(())?
.parse::<isize>()
.map_err(|_| ())?;
integer *= (10 as isize).pow(D as u32);
let field = match splitted.next() {
Some(s) => s,
None => return T::try_from(integer).map(|v| Self(v)).map_err(|_| ()),
};
let decimal_length = core::cmp::min(field.len(), 255) as u8;
let mut decimal = field.parse::<isize>().map_err(|_| ())?;
if integer < 0 || negative {
decimal = -decimal
}
if D >= decimal_length {
decimal *= (10 as isize).pow((D - decimal_length) as u32);
} else {
decimal /= (10 as isize).pow((decimal_length - D) as u32);
}
T::try_from(integer + decimal)
.map(|v| Self(v))
.map_err(|_| ())
}
}
impl<T, const D: u8> Display for FixedPoint<T, D>
where
T: Copy
+ Display
+ Into<i32>
+ PartialEq
+ Number
+ PartialOrd
+ Pow<u32, Output = T>
+ ops::Div<Output = T>
+ ops::Rem<Output = T>,
{
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
let mut decimal = self.decimal().into().abs();
if D == 0 || decimal == 0 {
return write!(f, "{}.0", self.integer());
}
let mut length = D;
while decimal % 10 == 0 {
decimal = decimal / 10;
length -= 1;
}
let integer = self.integer();
if integer == T::zero() && self.0 < T::zero() {
write!(f, "-0.{:0length$}", decimal, length = length as usize)
} else {
write!(
f,
"{}.{:0length$}",
integer,
decimal,
length = length as usize
)
}
}
}
#[cfg(feature = "serde")]
impl<T: Copy + Into<i32>, const D: u8> serde::Serialize for FixedPoint<T, D> {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_f32((*self).into())
}
}
#[cfg(feature = "serde")]
impl<'a, T: convert::TryFrom<isize>, const D: u8> serde::Deserialize<'a> for FixedPoint<T, D> {
fn deserialize<DE: serde::Deserializer<'a>>(deserializer: DE) -> Result<Self, DE::Error> {
let float = <f32>::deserialize(deserializer)?;
let v = (float * 10f32.powi(D as i32)) as isize;
T::try_from(v)
.map(|v| Self(v))
.map_err(|_| <DE::Error as serde::de::Error>::custom("Not fixed-point"))
}
}