irox_fixedmath/
lib.rs

1// SPDX-License-Identifier: MIT
2// Copyright 2025 IROX Contributors
3//
4
5//!
6//! Fixed-precision mathematics.
7//!
8
9#![forbid(unsafe_code)]
10#![no_std]
11
12use core::cmp::Ordering;
13use core::fmt::Formatter;
14use core::str::FromStr;
15pub use irox_tools::f64::FloatExt;
16
17macro_rules! consts {
18    ($prim:ty, $val:literal, $shift:ident, $valt:ident, $mask:ident) => {
19        const $shift: $prim = $val;
20        const $valt: $prim = 1 << $shift;
21        const $mask: $prim = $valt - 1;
22    };
23}
24consts!(u32, 16, U32_SHIFT, U32_VAL, U32_MASK);
25consts!(i32, 16, I32_SHIFT, I32_VAL, I32_MASK);
26consts!(u64, 32, U64_SHIFT, U64_VAL, U64_MASK);
27consts!(i64, 32, I64_SHIFT, I64_VAL, I64_MASK);
28consts!(u128, 64, U128_SHIFT, U128_VAL, U128_MASK);
29consts!(i128, 64, I128_SHIFT, I128_VAL, I128_MASK);
30
31macro_rules! impl_partial_cmp_eq {
32    ($prim:ty, $($typ:tt)+) => {
33        impl core::cmp::PartialEq<$prim> for $($typ)+ {
34            fn eq(&self, other: &$prim) -> bool {
35                <$prim>::from(*self) == *other
36            }
37        }
38        impl core::cmp::PartialOrd<$prim> for $($typ)+ {
39            fn partial_cmp(&self, other: &$prim) -> Option<core::cmp::Ordering> {
40                <$prim>::partial_cmp(&self.into(), &other)
41            }
42        }
43    };
44}
45
46macro_rules! impl_fromf64 {
47    ($shift:ident, $val:ident, $mask:ident, $($typ:tt)+) => {
48        impl From<$($typ)+> for f64 {
49            fn from(value: $($typ)+) -> Self {
50                let val = (value.data >> $shift) as f64;
51                val + ((value.data & $mask) as f64 / $val as f64)
52            }
53        }
54    };
55}
56macro_rules! impl_from {
57    ($prim:ty, $shift:expr, $($typ:tt)+) => {
58        impl From<$($typ)+> for $prim {
59            fn from(value: $($typ)+) -> Self {
60                value.data >> $shift
61            }
62        }
63    };
64}
65
66macro_rules! impl_ops {
67    ($strukt:ty, $prim:ty, $next_prim:ty, $shift:ident, $($typ:tt)+) => {
68        impl core::ops::Add<$strukt> for $($typ)+ {
69            type Output = $strukt;
70
71            fn add(self, rhs: $strukt) -> Self::Output {
72                <$strukt>::from_raw_value(self.data.saturating_add(rhs.data))
73            }
74        }
75        impl core::ops::Sub<$strukt> for $($typ)+ {
76            type Output = $strukt;
77
78            fn sub(self, rhs: $strukt) -> Self::Output {
79                <$strukt>::from_raw_value(self.data.saturating_sub(rhs.data))
80            }
81        }
82        impl core::ops::AddAssign<$($typ)+> for $strukt {
83            fn add_assign(&mut self, rhs: $($typ)+) {
84                self.data = self.data.saturating_add(rhs.data)
85            }
86        }
87
88        impl core::ops::AddAssign<$($typ)+> for &mut $strukt {
89            fn add_assign(&mut self, rhs: $($typ)+) {
90                self.data = self.data.saturating_add(rhs.data)
91            }
92        }
93        impl core::ops::SubAssign<$($typ)+> for $strukt {
94            fn sub_assign(&mut self, rhs: $($typ)+) {
95                self.data = self.data.saturating_sub(rhs.data)
96            }
97        }
98
99        impl core::ops::SubAssign<$($typ)+> for &mut $strukt {
100            fn sub_assign(&mut self, rhs: $($typ)+) {
101                self.data = self.data.saturating_sub(rhs.data)
102            }
103        }
104        impl core::ops::Mul<$($typ)+> for $strukt {
105            type Output = $strukt;
106
107            fn mul(self, rhs: $($typ)+) -> Self::Output {
108                let o = (self.data as $next_prim * rhs.data as $next_prim) >> ($shift - 1);
109                let add = o & 0x01;
110                let o = (o >> 1) + add;
111                <$strukt>::from_raw_value(o as $prim)
112            }
113        }
114
115        impl core::ops::Mul<$($typ)+> for &mut $strukt {
116            type Output = $strukt;
117
118            fn mul(self, rhs: $($typ)+) -> Self::Output {
119                let o = ((self.data as $next_prim).saturating_mul(rhs.data as $next_prim)) >> ($shift - 1);
120                let add = o & 0x01;
121                let o = (o >> 1) + add;
122                <$strukt>::from_raw_value(o as $prim)
123            }
124        }
125        impl core::ops::Div<$($typ)+> for $strukt {
126            type Output = $strukt;
127
128            fn div(self, rhs: $($typ)+) -> Self::Output {
129                let a = (self.data as $next_prim) << $shift;
130                let b = (rhs.data as $next_prim);
131                let o = (a / b) as $prim;
132                <$strukt>::from_raw_value(o)
133            }
134        }
135    };
136}
137macro_rules! impl_mut_ops {
138    ($strukt:ty, $prim:ty, $next_prim:ty, $shift:ident) => {
139        impl core::ops::MulAssign for $strukt {
140            fn mul_assign(&mut self, rhs: $strukt) {
141                self.data = core::ops::Mul::mul(*self, rhs).data;
142            }
143        }
144        impl core::ops::MulAssign for &mut $strukt {
145            fn mul_assign(&mut self, rhs: &mut $strukt) {
146                self.data = core::ops::Mul::mul(**self, rhs).data;
147            }
148        }
149        impl core::ops::DivAssign for $strukt {
150            fn div_assign(&mut self, rhs: $strukt) {
151                self.data = core::ops::Div::div(*self, rhs).data;
152            }
153        }
154        impl core::ops::DivAssign for &mut $strukt {
155            fn div_assign(&mut self, rhs: &mut $strukt) {
156                self.data = core::ops::Div::div(**self, rhs).data;
157            }
158        }
159    };
160}
161macro_rules! impl_fmt_as_f64 {
162    ($typ:ty, $f:path) => {
163        impl $f for $typ {
164            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
165                <dyn $f>::fmt(&Into::<f64>::into(self), f)
166            }
167        }
168    };
169}
170macro_rules! impl_fmt_as_inner {
171    ($typ:ty, $f:path) => {
172        impl $f for $typ {
173            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
174                <dyn $f>::fmt(&self.data, f)
175            }
176        }
177    };
178}
179
180macro_rules! impl_unsigned_flops {
181    ($typ:ty, $prim:ty, $lower_prim:ty, $shift:ident, $val:ident, $mask:ident) => {
182        impl core::ops::Add<f64> for $typ {
183            type Output = Self;
184            fn add(self, rhs: f64) -> Self::Output {
185                let v = <$typ>::from(rhs);
186                self + v
187            }
188        }
189        impl core::ops::AddAssign<f64> for $typ {
190            fn add_assign(&mut self, rhs: f64) {
191                *self = *self + rhs;
192            }
193        }
194        impl core::ops::AddAssign<f64> for &mut $typ {
195            fn add_assign(&mut self, rhs: f64) {
196                **self = **self + rhs;
197            }
198        }
199        impl core::ops::Sub<f64> for $typ {
200            type Output = Self;
201
202            fn sub(self, rhs: f64) -> Self::Output {
203                let v = <$typ>::from(rhs);
204                self - v
205            }
206        }
207        impl irox_tools::f64::FloatExt for $typ {
208            type Type = Self;
209            type Size = $prim;
210
211            fn trunc(self) -> Self::Type {
212                // just mask out the fractional bits leaving the whole bits.
213                Self::from_raw_value(self.data & ($mask << $shift))
214            }
215
216            fn fract(self) -> Self::Type {
217                // just mask out the whole bits leaving the fractional bits.
218                Self::from_raw_value(self.data & $mask)
219            }
220
221            fn abs(self) -> Self::Type {
222                // no change for unsigned flops.
223                self
224            }
225
226            fn round(self) -> Self::Type {
227                (self + Self::Type::ONE_HALF).trunc()
228            }
229
230            fn floor(self) -> Self::Type {
231                // same as trunc for unsigned
232                self.trunc()
233            }
234
235            fn ceil(self) -> Self::Type {
236                if Self::Type::fract(&self) == 0 {
237                    return self;
238                }
239                Self::Type::from_parts(self.whole() + 1, 0)
240            }
241
242            fn signum(self) -> Self::Type {
243                1.into()
244            }
245
246            ///
247            /// Implementation of Exponential Function from NIST DTMF eq 4.2.19: `<https://dlmf.nist.gov/4.2.E19>`
248            fn exp(self) -> Self::Type {
249                let mut out = Self::from_parts(1, 0);
250                let i = self;
251                let mut idx = 1u16;
252                let mut next = self;
253
254                while next.abs() != 0.0 {
255                    out += next;
256                    idx += 1;
257                    next *= i / idx;
258                }
259
260                out
261            }
262
263            ///
264            /// Implementation of Natural Logarithm using NIST DLMF eq 4.6.4: `<https://dlmf.nist.gov/4.6.E4>`
265            fn ln(self) -> Self::Type {
266                let z = self;
267                if z == 0. {
268                    return Self::Type::from_parts(1, 0);
269                }
270                let iter = (z - 1u8) / (z + 1u8);
271                let mut out = Self::Type::default();
272                let mut next = iter * 2u8;
273                let mut idx = 1 as $lower_prim;
274                let mut base = iter;
275                while !next.is_zero() {
276                    out += next;
277                    idx += 2;
278                    base *= iter * iter;
279                    next = (base * 2 as $lower_prim) / idx;
280                }
281                out
282            }
283
284            fn powi(self, val: i32) -> Self::Type {
285                let mut out = self;
286                let i = self;
287                for _ in 0..val.abs() {
288                    out *= i;
289                }
290                out
291            }
292
293            ///
294            /// Implementation of general power function using NIST DLMF eq 4.2.26: `<https://dlmf.nist.gov/4.2.E26>`
295            fn powf(self, a: Self::Type) -> Self::Type {
296                let z = self;
297
298                (a * z.ln()).exp()
299            }
300
301            fn sqrt(self) -> Self::Type {
302                self.powf(0.5.into())
303            }
304            fn to_bits(self) -> Self::Size {
305                self.raw_value()
306            }
307
308            fn exponent(self) -> u16 {
309                irox_tools::f64::FloatExt::exponent(self.as_f64())
310            }
311
312            fn significand(self) -> Self::Size {
313                irox_tools::f64::FloatExt::significand(self.as_f64()) as $prim
314            }
315        }
316    };
317}
318macro_rules! impl_signed_flops {
319    ($typ:ty, $prim:ty, $lower_prim:ty, $shift:ident, $val:ident, $mask:ident) => {
320        impl core::ops::Add<f64> for $typ {
321            type Output = Self;
322            fn add(self, rhs: f64) -> Self::Output {
323                let v = <$typ>::from(rhs);
324                self + v
325            }
326        }
327        impl core::ops::AddAssign<f64> for $typ {
328            fn add_assign(&mut self, rhs: f64) {
329                *self = *self + rhs;
330            }
331        }
332        impl core::ops::AddAssign<f64> for &mut $typ {
333            fn add_assign(&mut self, rhs: f64) {
334                **self = **self + rhs;
335            }
336        }
337        impl core::ops::Sub<f64> for $typ {
338            type Output = Self;
339
340            fn sub(self, rhs: f64) -> Self::Output {
341                let v = <$typ>::from(rhs);
342                self - v
343            }
344        }
345        impl irox_tools::f64::FloatExt for $typ {
346            type Type = Self;
347            type Size = $prim;
348
349            fn trunc(self) -> Self::Type {
350                // just mask out the fractional bits leaving the whole bits.
351                Self::from_raw_value(self.data & ($mask << $shift))
352            }
353
354            fn fract(self) -> Self::Type {
355                // just mask out the whole bits leaving the fractional bits.
356                Self::from_raw_value(self.data & $mask)
357            }
358
359            fn abs(self) -> Self::Type {
360                let bm = $mask | ($mask << $shift);
361
362                Self::from_raw_value(self.data & bm)
363            }
364
365            fn round(self) -> Self::Type {
366                (self + Self::Type::ONE_HALF).trunc()
367            }
368
369            fn floor(self) -> Self::Type {
370                todo!()
371            }
372
373            fn ceil(self) -> Self::Type {
374                if Self::Type::fract(&self) == 0 {
375                    return self;
376                }
377                Self::Type::from_parts(self.whole() + 1, 0)
378            }
379
380            fn signum(self) -> Self::Type {
381                1.into()
382            }
383
384            ///
385            /// Implementation of Exponential Function from NIST DTMF eq 4.2.19: `<https://dlmf.nist.gov/4.2.E19>`
386            fn exp(self) -> Self::Type {
387                let mut out = Self::from_parts(1, 0);
388                let i = self;
389                let mut idx = 1u16;
390                let mut next = self;
391
392                while next.abs() != 0.0 {
393                    out += next;
394                    idx += 1;
395                    next *= i / idx;
396                }
397
398                out
399            }
400
401            ///
402            /// Implementation of Natural Logarithm using NIST DLMF eq 4.6.4: `<https://dlmf.nist.gov/4.6.E4>`
403            fn ln(self) -> Self::Type {
404                let z = self;
405                if z == 0. {
406                    return Self::Type::from_parts(1, 0);
407                }
408                let iter = (z - 1u8) / (z + 1u8);
409                let mut out = Self::Type::default();
410                let mut next = iter * 2u8;
411                let mut idx = 1 as $lower_prim;
412                let mut base = iter;
413                while !next.is_zero() {
414                    out += next;
415                    idx += 2;
416                    base *= iter * iter;
417                    next = (base * 2 as $lower_prim) / idx;
418                }
419                out
420            }
421
422            fn powi(self, val: i32) -> Self::Type {
423                let mut out = self;
424                let i = self;
425                for _ in 0..val.abs() {
426                    out *= i;
427                }
428                out
429            }
430
431            ///
432            /// Implementation of general power function using NIST DLMF eq 4.2.26: `<https://dlmf.nist.gov/4.2.E26>`
433            fn powf(self, a: Self::Type) -> Self::Type {
434                let z = self;
435
436                (a * z.ln()).exp()
437            }
438
439            fn sqrt(self) -> Self::Type {
440                self.powf(0.5.into())
441            }
442            fn to_bits(self) -> Self::Size {
443                self.raw_value()
444            }
445
446            fn exponent(self) -> u16 {
447                irox_tools::f64::FloatExt::exponent(self.as_f64())
448            }
449
450            fn significand(self) -> Self::Size {
451                irox_tools::f64::FloatExt::significand(self.as_f64()) as $prim
452            }
453        }
454    };
455}
456macro_rules! impl_prim_ops {
457    ($typ:ty, $prim:ty, $rhs:ty) => {
458        impl core::ops::Add<$rhs> for $typ {
459            type Output = Self;
460
461            fn add(self, rhs: $rhs) -> Self::Output {
462                self + Self::from_parts(rhs as $prim, 0)
463            }
464        }
465        impl core::ops::Add<$typ> for $rhs {
466            type Output = $typ;
467
468            fn add(self, rhs: $typ) -> Self::Output {
469                rhs + self
470            }
471        }
472        impl core::ops::Sub<$rhs> for $typ {
473            type Output = Self;
474
475            fn sub(self, rhs: $rhs) -> Self::Output {
476                self - Self::from_parts(rhs as $prim, 0)
477            }
478        }
479        impl core::ops::Sub<$typ> for $rhs {
480            type Output = $typ;
481
482            fn sub(self, rhs: $typ) -> Self::Output {
483                <$typ>::from_parts(self as $prim, 0) - rhs
484            }
485        }
486        impl core::ops::Mul<$rhs> for $typ {
487            type Output = Self;
488
489            fn mul(self, rhs: $rhs) -> Self::Output {
490                self * Self::from_parts(rhs as $prim, 0)
491            }
492        }
493        impl core::ops::Mul<$typ> for $rhs {
494            type Output = $typ;
495
496            fn mul(self, rhs: $typ) -> Self::Output {
497                rhs * self
498            }
499        }
500        impl core::ops::Div<$rhs> for $typ {
501            type Output = Self;
502
503            fn div(self, rhs: $rhs) -> Self::Output {
504                self / Self::from_parts(rhs as $prim, 0)
505            }
506        }
507        impl core::ops::Div<$typ> for $rhs {
508            type Output = $typ;
509
510            fn div(self, rhs: $typ) -> Self::Output {
511                <$typ>::from_parts(self as $prim, 0) / rhs
512            }
513        }
514        impl core::cmp::PartialEq<$rhs> for $typ {
515            fn eq(&self, other: &$rhs) -> bool {
516                (*self).eq(&Self::from_parts(*other as $prim, 0))
517            }
518        }
519        impl core::cmp::PartialEq<$typ> for $rhs {
520            fn eq(&self, other: &$typ) -> bool {
521                <$typ>::from_parts(*self as $prim, 0).eq(other)
522            }
523        }
524        impl core::cmp::PartialOrd<$rhs> for $typ {
525            fn partial_cmp(&self, other: &$rhs) -> Option<Ordering> {
526                self.partial_cmp(&Self::from_parts(*other as $prim, 0))
527            }
528        }
529        impl core::cmp::PartialOrd<$typ> for $rhs {
530            fn partial_cmp(&self, other: &$typ) -> Option<Ordering> {
531                <$typ>::from_parts(*self as $prim, 0).partial_cmp(other)
532            }
533        }
534    };
535}
536macro_rules! impl_base {
537    ($typ:ty, $prim:ty, $lower_prim:ty, $next_prim:ty, $shift:ident, $val:ident, $mask:ident) => {
538        impl_fmt_as_f64!($typ, core::fmt::Display);
539        impl_fmt_as_f64!($typ, core::fmt::LowerExp);
540        impl_fmt_as_f64!($typ, core::fmt::UpperExp);
541        impl_fmt_as_inner!($typ, core::fmt::LowerHex);
542        impl_fmt_as_inner!($typ, core::fmt::UpperHex);
543
544        impl core::fmt::Debug for $typ {
545            fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
546                let w = self.whole();
547                let p = self.fract();
548                f.write_fmt(format_args!("{}: {}/{}", stringify!($typ), w, p))
549            }
550        }
551
552        impl $typ {
553            pub const fn from_parts(whole: $lower_prim, fracs: $lower_prim) -> Self {
554                Self {
555                    data: (whole as $prim) << $shift | (fracs as $prim),
556                }
557            }
558            pub const fn from_raw_value(data: $prim) -> Self {
559                Self { data }
560            }
561            pub const fn trunc(&self) -> $lower_prim {
562                (self.data >> $shift) as $lower_prim
563            }
564            pub const fn whole(&self) -> $lower_prim {
565                self.trunc()
566            }
567            pub const fn fract(&self) -> $lower_prim {
568                (self.data & $mask) as $lower_prim
569            }
570            pub const fn raw_value(&self) -> $prim {
571                self.data
572            }
573            pub fn as_f64(&self) -> f64 {
574                self.into()
575            }
576            pub const fn is_zero(&self) -> bool {
577                self.data == 0 as $prim
578            }
579        }
580
581        impl From<$prim> for $typ {
582            fn from(data: $prim) -> Self {
583                Self {
584                    data: data << $shift,
585                }
586            }
587        }
588        impl From<f64> for $typ {
589            fn from(value: f64) -> Self {
590                let w = irox_tools::f64::FloatExt::floor(value) as $lower_prim;
591                let f = irox_tools::f64::FloatExt::fract(value) * <$lower_prim>::MAX as f64;
592                let f = irox_tools::f64::FloatExt::round(f) as $lower_prim;
593                Self::from_parts(w, f)
594            }
595        }
596        impl From<f32> for $typ {
597            fn from(value: f32) -> Self {
598                From::<f64>::from(value as f64)
599            }
600        }
601        impl FromStr for $typ {
602            type Err = <f64 as core::str::FromStr>::Err;
603
604            fn from_str(s: &str) -> Result<Self, Self::Err> {
605                let v = <f64 as core::str::FromStr>::from_str(s)?;
606                Ok(v.into())
607            }
608        }
609        impl core::iter::Sum for $typ {
610            fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
611                let mut out = Self::default();
612                for v in iter {
613                    out += v;
614                }
615                out
616            }
617        }
618        impl<'a> core::iter::Sum<&'a $typ> for $typ {
619            fn sum<I: Iterator<Item = &'a $typ>>(iter: I) -> Self {
620                let mut out = Self::default();
621                for v in iter {
622                    out *= *v;
623                }
624                out
625            }
626        }
627        impl core::iter::Product for $typ {
628            fn product<I: Iterator<Item = Self>>(iter: I) -> Self {
629                let mut out = Self::default();
630                for v in iter {
631                    out *= v;
632                }
633                out
634            }
635        }
636        impl<'a> core::iter::Product<&'a $typ> for $typ {
637            fn product<I: Iterator<Item = &'a $typ>>(iter: I) -> Self {
638                let mut out = Self::default();
639                for v in iter {
640                    out *= *v;
641                }
642                out
643            }
644        }
645
646        impl_fromf64!($shift, $val, $mask, $typ);
647        impl_fromf64!($shift, $val, $mask, &$typ);
648        impl_fromf64!($shift, $val, $mask, &mut $typ);
649        impl_from!($prim, $shift, $typ);
650        impl_from!($prim, $shift, &$typ);
651        impl_from!($prim, $shift, &mut $typ);
652        impl_ops!($typ, $prim, $next_prim, $shift, $typ);
653        impl_ops!($typ, $prim, $next_prim, $shift, &$typ);
654        impl_ops!($typ, $prim, $next_prim, $shift, &mut $typ);
655
656        impl_mut_ops!($typ, $prim, $next_prim, $shift);
657
658        impl_partial_cmp_eq!(f64, $typ);
659    };
660}
661
662///
663/// Fixed precision [`u32`] - upper 16 bits is value, lower 16 bits are the fractional portion of the
664/// value.  Each fractional portion is 1/[`u16::MAX`] ~= `1.5259e-5` or `0.000_015_259`, or about
665/// `15.3 micro`, and can accurately represent SI-prefixes: `milli/1e-3`. The
666/// whole portion can represent `0` -> [`u16::MAX`] (`65_535`)
667#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
668pub struct FixedU32 {
669    data: u32,
670}
671impl FixedU32 {
672    pub const E: FixedU32 = FixedU32::from_parts(2, 47_073);
673    pub const PI: FixedU32 = FixedU32::from_parts(3, 9_279);
674    pub const ONE_HALF: FixedU32 = FixedU32::from_parts(0, 32_768);
675    pub const RESOLUTION: FixedU32 = FixedU32::from_parts(0, 1);
676}
677impl_base!(FixedU32, u32, u16, u64, U32_SHIFT, U32_VAL, U32_MASK);
678impl_prim_ops!(FixedU32, u16, u8);
679impl_prim_ops!(FixedU32, u16, u16);
680impl_unsigned_flops!(FixedU32, u32, u16, U32_SHIFT, U32_VAL, U32_MASK);
681
682///
683/// Fixed precision [`i32`] - upper 16 bits is value, lower 16 bits are the fractional portion of the
684/// value.  Each fractional portion is 1/[`u16::MAX`] ~= `1.5259e-5` or `0.000_015_259`, or about
685/// `15.3 micro`, and can accurately represent SI-prefixes: `milli/1e-3`. The
686/// whole portion can represent [`i16::MIN`] (`-32768`) -> [`i16::MAX`] (`32_727`)
687#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
688pub struct FixedI32 {
689    data: i32,
690}
691impl_base!(FixedI32, i32, i16, i64, I32_SHIFT, I32_VAL, I32_MASK);
692impl_prim_ops!(FixedI32, i16, u8);
693impl_prim_ops!(FixedI32, i16, u16);
694
695///
696/// Fixed precision [`u64`] - upper 32 bits is value, lower 32 bits are the fractional portion of the
697/// value.  Each fractional portion is 1/[`u32::MAX`] ~= `2.328306e-10` or `0.000_000_000_238_306`,
698/// or about `238.3 pico`, and can accurately represent SI-prefixes `milli/1e-3`, `micro/1e-6`, and
699/// `nano/1e-9`. The whole portion can represent `0` -> [`u32::MAX`] (`4_294_967_295`)
700#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
701pub struct FixedU64 {
702    data: u64,
703}
704impl FixedU64 {
705    pub const E: FixedU64 = FixedU64::from_parts(2, 3_084_996_962);
706    pub const PI: FixedU64 = FixedU64::from_parts(3, 608_135_816);
707    pub const ONE_HALF: FixedU64 = FixedU64::from_parts(0, 2_147_483_648);
708    pub const RESOLUTION: FixedU64 = FixedU64::from_parts(0, 1);
709}
710impl_base!(FixedU64, u64, u32, u128, U64_SHIFT, U64_VAL, U64_MASK);
711impl_unsigned_flops!(FixedU64, u64, u32, U64_SHIFT, U64_VAL, U64_MASK);
712impl_prim_ops!(FixedU64, u32, u8);
713impl_prim_ops!(FixedU64, u32, u16);
714impl_prim_ops!(FixedU64, u32, u32);
715
716///
717/// Fixed precision [`i64`] - upper 32 bits is value, lower 32 bits are the fractional portion of the
718/// value.  Each fractional portion is 1/[`u32::MAX`] ~= `2.328306e-10` or `0.000_000_000_238_306`,
719/// or about `238.3 pico`, and can accurately represent SI-prefixes `milli/1e-3`, `micro/1e-6`, and
720/// `nano/1e-9`. The whole portion can represent [`i32::MIN`] (`-2_147_483_648`) -> [`i32::MAX`] (`2_147_483_647`)
721#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
722pub struct FixedI64 {
723    data: i64,
724}
725impl_base!(FixedI64, i64, i32, i128, I64_SHIFT, I64_VAL, I64_MASK);
726impl_prim_ops!(FixedI64, i32, u8);
727impl_prim_ops!(FixedI64, i32, u16);
728impl_prim_ops!(FixedI64, i32, u32);
729
730///
731/// Fixed precision [`u128`] - upper 64 bits is value, lower 64 bits are the fractional portion of the
732/// value.  Each fractional portion is 1/[`u64::MAX`] ~= `5.4210e-20` or
733/// `0.000_000_000_000_000_000_054_210`, or about `54.2 zepto`, and can
734/// accurately represent SI-prefixes `milli/1e-3`, `micro/1e-6`, `nano/1e-9`,
735/// `pico/1e-12`, `femto/1e-15`, and `atto/1e-18`.  This is probably overkill
736/// for what you need.  The whole portion can represent `0` -> [`u64::MAX`] (`1.84467E+19`)
737#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
738pub struct FixedU128 {
739    data: u128,
740}
741impl FixedU128 {
742    pub const E: FixedU128 = FixedU128::from_parts(2, 13_249_961_062_380_153_450);
743    pub const PI: FixedU128 = FixedU128::from_parts(3, 2_611_923_443_488_327_891);
744    pub const ONE_HALF: FixedU128 = FixedU128::from_parts(0, 9_223_372_036_854_775_808);
745    pub const RESOLUTION: FixedU128 = FixedU128::from_parts(0, 1);
746}
747impl_base!(FixedU128, u128, u64, u128, U128_SHIFT, U128_VAL, U128_MASK);
748impl_unsigned_flops!(FixedU128, u128, u64, U128_SHIFT, U128_VAL, U128_MASK);
749impl_prim_ops!(FixedU128, u64, u8);
750impl_prim_ops!(FixedU128, u64, u16);
751impl_prim_ops!(FixedU128, u64, u32);
752impl_prim_ops!(FixedU128, u64, u64);
753
754///
755/// Fixed precision [`u128`] - upper 64 bits is value, lower 64 bits are the fractional portion of the
756/// value.  Each fractional portion is 1/[`i64::MAX`] ~= `5.4210e-20` or
757/// `0.000_000_000_000_000_000_054_210`, or about `54.2 zepto`, and can
758/// accurately represent SI-prefixes `milli/1e-3`, `micro/1e-6`, `nano/1e-9`,
759/// `pico/1e-12`, `femto/1e-15`, and `atto/1e-18`.  This is probably overkill
760/// for what you need.  The whole portion can represent
761/// [`i64::MIN`] (`-9_223_372_036_854_775_808`) -> [`i64::MAX`] (`9_223_372_036_854_775_807`)
762#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
763pub struct FixedI128 {
764    data: i128,
765}
766impl FixedI128 {
767    // pub const E: FixedI128 = FixedI128::from_parts(2, 13_249_961_062_380_153_450);
768    pub const PI: FixedI128 = FixedI128::from_parts(3, 2_611_923_443_488_327_891);
769    pub const ONE_HALF: FixedI128 = FixedI128::from_parts(0, 9_223_372_036_854_775_807);
770    pub const RESOLUTION: FixedI128 = FixedI128::from_parts(0, 1);
771}
772impl_base!(FixedI128, i128, i64, i128, I128_SHIFT, I128_VAL, I128_MASK);
773impl_signed_flops!(FixedI128, i128, i64, I128_SHIFT, I128_VAL, I128_MASK);
774impl_prim_ops!(FixedI128, i64, u8);
775impl_prim_ops!(FixedI128, i64, i8);
776impl_prim_ops!(FixedI128, i64, u16);
777impl_prim_ops!(FixedI128, i64, i16);
778impl_prim_ops!(FixedI128, i64, u32);
779impl_prim_ops!(FixedI128, i64, i32);
780impl_prim_ops!(FixedI128, i64, u64);
781impl_prim_ops!(FixedI128, i64, i64);
782
783pub trait AsFixedPoint {
784    fn as_fixed_u32(&self) -> FixedU32;
785    fn as_fixed_i32(&self) -> FixedI32;
786    fn as_fixed_u64(&self) -> FixedU64;
787    fn as_fixed_i64(&self) -> FixedI64;
788    fn as_fixed_u128(&self) -> FixedU128;
789    fn as_fixed_i128(&self) -> FixedI128;
790}
791macro_rules! impl_as_fixedpt {
792    ($ty:ty) => {
793        impl AsFixedPoint for $ty {
794            fn as_fixed_u32(&self) -> FixedU32 {
795                FixedU32::from(*self)
796            }
797
798            fn as_fixed_i32(&self) -> FixedI32 {
799                FixedI32::from(*self)
800            }
801
802            fn as_fixed_u64(&self) -> FixedU64 {
803                FixedU64::from(*self)
804            }
805
806            fn as_fixed_i64(&self) -> FixedI64 {
807                FixedI64::from(*self)
808            }
809
810            fn as_fixed_u128(&self) -> FixedU128 {
811                FixedU128::from(*self)
812            }
813
814            fn as_fixed_i128(&self) -> FixedI128 {
815                FixedI128::from(*self)
816            }
817        }
818    };
819}
820impl_as_fixedpt!(f32);
821impl_as_fixedpt!(f64);