Skip to main content

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