use core::ops::*;
#[allow(non_camel_case_types)]
#[cfg(not(feature = "fixed"))]
pub type i16fx8 = Fixed<i16, 8>;
#[allow(non_camel_case_types)]
#[cfg(not(feature = "fixed"))]
pub type i16fx14 = Fixed<i16, 14>;
#[allow(non_camel_case_types)]
#[cfg(not(feature = "fixed"))]
pub type i32fx8 = Fixed<i32, 8>;
#[allow(non_camel_case_types)]
#[cfg(feature = "fixed")]
pub type i16fx8 = ::fixed::FixedI32<::fixed::types::extra::U8>;
#[allow(non_camel_case_types)]
#[cfg(feature = "fixed")]
pub type i16fx14 = ::fixed::FixedI16<::fixed::types::extra::U14>;
#[allow(non_camel_case_types)]
#[cfg(feature = "fixed")]
pub type i32fx8 = ::fixed::FixedI32<::fixed::types::extra::U8>;
#[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct Fixed<I, const B: u32>(I);
macro_rules! impl_trait_op_unit {
($t:ty, $trait:ident, $op:ident) => {
impl<const B: u32> $trait for Fixed<$t, B> {
type Output = Self;
#[inline]
#[must_use]
#[cfg_attr(feature = "track_caller", track_caller)]
fn $op(self) -> Self::Output {
Self::$op(self)
}
}
};
}
macro_rules! impl_trait_op_self_rhs {
($t:ty, $trait:ident, $op:ident) => {
impl<const B: u32> $trait for Fixed<$t, B> {
type Output = Self;
#[inline]
#[must_use]
#[cfg_attr(feature = "track_caller", track_caller)]
fn $op(self, rhs: Self) -> Self::Output {
Self::$op(self, rhs)
}
}
};
}
macro_rules! impl_trait_op_assign_self_rhs {
($t:ty, $trait:ident, $op:ident, $op_assign:ident) => {
impl<const B: u32> $trait for Fixed<$t, B> {
#[inline]
#[cfg_attr(feature = "track_caller", track_caller)]
fn $op_assign(&mut self, rhs: Self) {
*self = self.$op(rhs);
}
}
};
}
macro_rules! impl_shift_self_u32 {
($t:ty, $trait:ident, $op:ident) => {
impl<const B: u32> $trait<u32> for Fixed<$t, B> {
type Output = Self;
#[inline]
#[must_use]
#[cfg_attr(feature = "track_caller", track_caller)]
fn $op(self, rhs: u32) -> Self::Output {
Self::$op(self, rhs)
}
}
};
}
macro_rules! impl_shift_assign_self_u32 {
($t:ty, $trait:ident, $op:ident, $op_assign:ident) => {
impl<const B: u32> $trait<u32> for Fixed<$t, B> {
#[inline]
#[cfg_attr(feature = "track_caller", track_caller)]
fn $op_assign(&mut self, rhs: u32) {
*self = self.$op(rhs);
}
}
};
}
macro_rules! impl_common_fixed_ops {
($t:ty) => {
impl<const B: u32> Fixed<$t, B> {
#[inline]
#[must_use]
#[cfg_attr(feature = "track_caller", track_caller)]
pub const fn wrapping_from(i: $t) -> Self {
Self(i << B)
}
#[inline]
#[must_use]
#[cfg_attr(feature = "track_caller", track_caller)]
pub const fn from_bits(i: $t) -> Self {
Self(i)
}
#[inline]
#[must_use]
#[cfg_attr(feature = "track_caller", track_caller)]
pub const fn to_bits(self) -> $t {
self.0
}
#[inline]
#[must_use]
#[cfg_attr(feature = "track_caller", track_caller)]
pub const fn not(self) -> Self {
Self(!self.0)
}
#[inline]
#[must_use]
#[cfg_attr(feature = "track_caller", track_caller)]
pub const fn add(self, rhs: Self) -> Self {
Self(self.0 + rhs.0)
}
#[inline]
#[must_use]
#[cfg_attr(feature = "track_caller", track_caller)]
pub const fn sub(self, rhs: Self) -> Self {
Self(self.0 - rhs.0)
}
#[inline]
#[must_use]
#[cfg_attr(feature = "track_caller", track_caller)]
pub const fn rem(self, rhs: Self) -> Self {
Self(self.0 % rhs.0)
}
#[inline]
#[must_use]
#[cfg_attr(feature = "track_caller", track_caller)]
pub const fn bitand(self, rhs: Self) -> Self {
Self(self.0 & rhs.0)
}
#[inline]
#[must_use]
#[cfg_attr(feature = "track_caller", track_caller)]
pub const fn bitor(self, rhs: Self) -> Self {
Self(self.0 | rhs.0)
}
#[inline]
#[must_use]
#[cfg_attr(feature = "track_caller", track_caller)]
pub const fn bitxor(self, rhs: Self) -> Self {
Self(self.0 ^ rhs.0)
}
#[inline]
#[must_use]
#[cfg_attr(feature = "track_caller", track_caller)]
pub const fn shl(self, rhs: u32) -> Self {
Self(self.0 << rhs)
}
#[inline]
#[must_use]
#[cfg_attr(feature = "track_caller", track_caller)]
pub const fn shr(self, rhs: u32) -> Self {
Self(self.0 >> rhs)
}
}
impl<const B: u32> core::fmt::Debug for Fixed<$t, B> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
let raw: $t = self.to_bits();
write!(
f,
concat!(
"Fixed::<",
stringify!($t),
",{}>::from_bits({:#x}_u32 as ",
stringify!($t),
")"
),
B, raw
)
}
}
impl_trait_op_unit!($t, Not, not);
impl_trait_op_self_rhs!($t, Add, add);
impl_trait_op_self_rhs!($t, Sub, sub);
impl_trait_op_self_rhs!($t, Mul, mul);
impl_trait_op_self_rhs!($t, Div, div);
impl_trait_op_self_rhs!($t, Rem, rem);
impl_trait_op_self_rhs!($t, BitAnd, bitand);
impl_trait_op_self_rhs!($t, BitOr, bitor);
impl_trait_op_self_rhs!($t, BitXor, bitxor);
impl_shift_self_u32!($t, Shl, shl);
impl_shift_self_u32!($t, Shr, shr);
impl_trait_op_assign_self_rhs!($t, AddAssign, add, add_assign);
impl_trait_op_assign_self_rhs!($t, SubAssign, sub, sub_assign);
impl_trait_op_assign_self_rhs!($t, MulAssign, mul, mul_assign);
impl_trait_op_assign_self_rhs!($t, DivAssign, div, div_assign);
impl_trait_op_assign_self_rhs!($t, RemAssign, rem, rem_assign);
impl_trait_op_assign_self_rhs!($t, BitAndAssign, bitand, bitand_assign);
impl_trait_op_assign_self_rhs!($t, BitOrAssign, bitor, bitor_assign);
impl_trait_op_assign_self_rhs!($t, BitXorAssign, bitxor, bitxor_assign);
impl_shift_assign_self_u32!($t, ShlAssign, shl, shl_assign);
impl_shift_assign_self_u32!($t, ShrAssign, shr, shr_assign);
};
}
impl_common_fixed_ops!(i8);
impl_common_fixed_ops!(i16);
impl_common_fixed_ops!(i32);
impl_common_fixed_ops!(u8);
impl_common_fixed_ops!(u16);
impl_common_fixed_ops!(u32);
macro_rules! impl_signed_fixed_ops {
($t:ty, $unsigned:ty) => {
impl<const B: u32> Fixed<$t, B> {
#[inline]
#[must_use]
#[cfg_attr(feature = "track_caller", track_caller)]
pub const fn abs(self) -> Self {
Self(self.0.abs())
}
#[inline]
#[must_use]
#[cfg_attr(feature = "track_caller", track_caller)]
pub const fn neg(self) -> Self {
Self(-self.0)
}
#[inline]
#[must_use]
#[cfg_attr(feature = "track_caller", track_caller)]
pub const fn is_negative(self) -> bool {
self.0 < 0
}
#[inline]
#[must_use]
#[cfg_attr(feature = "track_caller", track_caller)]
pub const fn mul(self, rhs: Self) -> Self {
let raw = (self.0 as i32) * (rhs.0 as i32);
Self((raw >> B) as $t)
}
#[inline]
#[must_use]
#[cfg_attr(feature = "track_caller", track_caller)]
pub const fn div(self, rhs: Self) -> Self {
let m = (self.0 as i32) * (1 << B);
let d = m / (rhs.0 as i32);
Self(d as $t)
}
#[inline]
#[must_use]
#[cfg_attr(feature = "track_caller", track_caller)]
pub const fn fract(self) -> Self {
let frac_mask = (<$unsigned>::MAX >> (<$t>::BITS - B));
Self((self.0.unsigned_abs() & frac_mask) as $t)
}
#[inline]
#[must_use]
#[cfg_attr(feature = "track_caller", track_caller)]
pub const fn trunc(self) -> Self {
Self(((self.0.unsigned_abs() >> B) << B) as $t)
}
}
impl_trait_op_unit!($t, Neg, neg);
impl<const B: u32> core::fmt::Display for Fixed<$t, B> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
let neg = self.to_bits() < 0;
fixed_fmt_abs::<B>(f, self.to_bits().abs() as u32, neg)
}
}
};
}
impl_signed_fixed_ops!(i8, u8);
impl_signed_fixed_ops!(i16, u16);
impl_signed_fixed_ops!(i32, u32);
macro_rules! impl_unsigned_fixed_ops {
($t:ty) => {
impl<const B: u32> Fixed<$t, B> {
#[inline]
#[must_use]
#[cfg_attr(feature = "track_caller", track_caller)]
pub const fn mul(self, rhs: Self) -> Self {
let raw = (self.0 as u32) * (rhs.0 as u32);
Self((raw >> B) as $t)
}
#[inline]
#[must_use]
#[cfg_attr(feature = "track_caller", track_caller)]
pub const fn div(self, rhs: Self) -> Self {
let m = (self.0 as u32) * (1 << B);
let d = m / (rhs.0 as u32);
Self(d as $t)
}
#[inline]
#[must_use]
#[cfg_attr(feature = "track_caller", track_caller)]
pub const fn fract(self) -> Self {
Self(self.0 & (<$t>::MAX >> (<$t>::BITS - B)))
}
#[inline]
#[must_use]
#[cfg_attr(feature = "track_caller", track_caller)]
pub const fn trunc(self) -> Self {
Self(self.0 & (<$t>::MAX << B))
}
}
impl<const B: u32> core::fmt::Display for Fixed<$t, B> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
fixed_fmt_abs::<B>(f, self.to_bits() as u32, false)
}
}
};
}
impl_unsigned_fixed_ops!(u8);
impl_unsigned_fixed_ops!(u16);
impl_unsigned_fixed_ops!(u32);
fn fixed_fmt_abs<const B: u32>(
f: &mut core::fmt::Formatter, abs: u32, neg: bool,
) -> core::fmt::Result {
let precision = f.precision().unwrap_or(const { ((B as usize) + 1) / 3 });
let width = f.width().unwrap_or(0).saturating_sub(precision + 1);
let fract = abs & ((1 << B) - 1);
let fract_dec = 10u32
.checked_pow(precision as u32)
.and_then(|digits| fract.checked_mul(digits))
.map(|x| (x >> B) as u64)
.unwrap_or_else(|| (fract as u64 * 10u64.pow(precision as u32) >> B));
let mut ones = (abs >> B) as i32;
if neg {
if ones != 0 {
ones = ones.neg()
} else {
let width = width.saturating_sub(2);
return write!(f, "{:width$}-0.{fract_dec:0precision$}", "");
}
}
write!(f, "{ones:width$}.{fract_dec:0precision$}")
}
#[cfg(test)]
mod test {
use crate::fixed::{i16fx14, i32fx8};
use core::{fmt::Write, str};
struct WriteBuf<const N: usize>([u8; N], usize);
impl<'a, const N: usize> Default for WriteBuf<N> {
fn default() -> Self {
Self([0u8; N], 0)
}
}
impl<const N: usize> Write for WriteBuf<N> {
fn write_str(&mut self, s: &str) -> core::fmt::Result {
let src = s.as_bytes();
let len = (self.0.len() - self.1).min(src.len());
self.0[self.1..self.1 + len].copy_from_slice(&src[..len]);
self.1 += len;
if len < src.len() {
Err(core::fmt::Error)
} else {
Ok(())
}
}
}
impl<const N: usize> WriteBuf<N> {
fn take(&mut self) -> &str {
let len = self.1;
self.1 = 0;
str::from_utf8(&self.0[..len]).unwrap()
}
}
#[test_case]
fn decimal_display() {
let mut wbuf = WriteBuf::<16>::default();
let x = i32fx8::from_bits(0x12345678);
write!(&mut wbuf, "{x}").unwrap();
assert_eq!(wbuf.take(), "1193046.468");
write!(&mut wbuf, "{x:11.1}").unwrap();
assert_eq!(wbuf.take(), " 1193046.4");
write!(&mut wbuf, "{x:1.6}").unwrap();
assert_eq!(wbuf.take(), "1193046.468750");
let x = x.neg();
write!(&mut wbuf, "{x}").unwrap();
assert_eq!(wbuf.take(), "-1193046.468");
let x = i16fx14::from_bits(0x6544 as i16);
write!(&mut wbuf, "{x}").unwrap();
assert_eq!(wbuf.take(), "1.58227");
let x = x.neg();
write!(&mut wbuf, "{x:.10}").unwrap();
assert_eq!(wbuf.take(), "-1.5822753906");
write!(&mut wbuf, "{x:9.1}").unwrap();
assert_eq!(wbuf.take(), " -1.5");
let x = x.add(i16fx14::wrapping_from(1));
write!(&mut wbuf, "{x:9.2}").unwrap();
assert_eq!(wbuf.take(), " -0.58");
}
}