Skip to main content

fix/
lib.rs

1//! Fixed-point number types.
2//!
3//! # What?
4//!
5//! Fixed-point is a number representation with a fixed number of digits before and after the radix
6//! point. This means that range is static rather than dynamic, as with floating-point. It also
7//! means that they can be represented as integers, with their scale tracked by the type system.
8//!
9//! In this library, the scale of a `Fix` is represented as two type-level integers: the base and
10//! the exponent. Any underlying integer primitive can be used to store the number. Arithmetic can
11//! be performed on these numbers, and they can be converted to different scale exponents.
12//!
13//! # Why?
14//!
15//! A classic example: let's sum 10 cents and 20 cents using floating-point. We expect a result of
16//! 30 cents.
17//!
18//! ```should_panic
19//! assert_eq!(0.30, 0.10 + 0.20);
20//! ```
21//!
22//! Wrong! We get an extra forty quintillionths of a dollar.
23//!
24//! ```text
25//! assertion failed: `(left == right)` (left: `0.3`, right: `0.30000000000000004`)'
26//! ```
27//!
28//! This is due to neither 0.1 nor 0.2 being exactly representable in base-2, just as a third can't
29//! be represented exactly in base-10. With `Fix`, we can choose the precision we want in base-10,
30//! at compile-time. In this case, hundredths of a dollar will do.
31//!
32//! ```
33//! use fix::aliases::si::Centi; // Fix<_, U10, N2>
34//! assert_eq!(Centi::new(0_30), Centi::new(0_10) + Centi::new(0_20));
35//! ```
36//!
37//! But decimal is inefficient for binary computers, right? Multiplying and dividing by 10 is
38//! slower than bit-shifting, but that's only needed when _moving_ the point. With `Fix`, this is
39//! only done explicitly with the `convert` method.
40//!
41//! ```
42//! use fix::aliases::si::{Centi, Milli};
43//! assert_eq!(Milli::new(0_300), Centi::new(0_30).convert());
44//! ```
45//!
46//! We can also choose a base-2 scale just as easily.
47//!
48//! ```
49//! use fix::aliases::iec::{Kibi, Mebi};
50//! assert_eq!(Kibi::new(1024), Mebi::new(1).convert());
51//! ```
52//!
53//! It's also worth noting that the type-level scale changes when multiplying and dividing,
54//! avoiding any implicit conversion.
55//!
56//! ```
57//! use fix::aliases::iec::{Gibi, Kibi, Mebi};
58//! assert_eq!(Mebi::new(3), Gibi::new(6) / Kibi::new(2));
59//! ```
60//!
61//! # `no_std`
62//!
63//! This crate is `no_std`.
64
65//#![no_std]
66
67pub extern crate muldiv;
68pub extern crate num_traits;
69#[cfg(feature = "typed-floats")]
70pub extern crate typed_floats;
71pub extern crate typenum;
72
73pub mod aliases;
74pub mod fix_value;
75pub mod prelude;
76pub mod util;
77
78use core::cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd};
79use core::fmt::{Debug, Display, Error, Formatter};
80use core::hash::{Hash, Hasher};
81use core::marker::PhantomData;
82use core::ops::{Add, Div, Mul, Neg, Rem, Sub};
83use core::ops::{AddAssign, DivAssign, MulAssign, RemAssign, SubAssign};
84
85use muldiv::MulDiv;
86use num_traits::{
87    CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, ConstZero, SaturatingAdd, SaturatingSub,
88};
89use paste::paste;
90use typenum::consts::{U10, Z0};
91use typenum::marker_traits::{Bit, Integer, Unsigned};
92use typenum::operator_aliases::{AbsVal, Diff, Le, Sum};
93use typenum::type_operators::{Abs, IsLess};
94
95/// Fixed-point number representing _Bits × Base <sup>Exp</sup>_.
96///
97/// - `Bits` is an integer primitive type, or any type which can be created from a type-level
98///   integer and exponentiated.
99/// - `Base` is an [`Unsigned`] type-level integer.
100/// - `Exp` is a signed type-level [`Integer`].
101///
102/// [`Unsigned`]: ../typenum/marker_traits/trait.Unsigned.html
103/// [`Integer`]: ../typenum/marker_traits/trait.Integer.html
104///
105/// # Summary of operations
106///
107/// Lower case variables represent values of _Bits_. Upper case _B_ and _E_ represent type-level
108/// integers _Base_ and _Exp_, respectively.
109///
110/// - _−(x B<sup>E</sup>) = (−x) B<sup>E</sup>_
111/// - _(x B<sup>E</sup>) + (y B<sup>E</sup>) = (x + y) B<sup>E</sup>_
112/// - _(x B<sup>E</sup>) − (y B<sup>E</sup>) = (x − y) B<sup>E</sup>_
113/// - _(x B<sup>E<sub>x</sub></sup>) × (y B<sup>E<sub>y</sub></sup>) =
114///   (x × y) B<sup>E<sub>x</sub> + E<sub>y</sub></sup>_
115/// - _(x B<sup>E<sub>x</sub></sup>) ÷ (y B<sup>E<sub>y</sub></sup>) =
116///   (x ÷ y) B<sup>E<sub>x</sub> − E<sub>y</sub></sup>_
117/// - _(x B<sup>E<sub>x</sub></sup>) % (y B<sup>E<sub>y</sub></sup>) =
118///   (x % y) B<sup>E<sub>x</sub></sup>_
119/// - _(x B<sup>E</sup>) × y = (x × y) B<sup>E</sup>_
120/// - _(x B<sup>E</sup>) ÷ y = (x ÷ y) B<sup>E</sup>_
121/// - _(x B<sup>E</sup>) % y = (x % y) B<sup>E</sup>_
122pub struct Fix<Bits, Base, Exp> {
123    /// The underlying integer.
124    pub bits: Bits,
125
126    marker: PhantomData<(Base, Exp)>,
127}
128
129impl<Bits, Base, Exp> Fix<Bits, Base, Exp> {
130    /// Creates a number.
131    ///
132    /// # Examples
133    ///
134    /// ```
135    /// use fix::aliases::si::{Kilo, Milli};
136    /// Milli::new(25); // 0.025
137    /// Kilo::new(25); // 25 000
138    /// ```
139    pub fn new(bits: Bits) -> Self {
140        Fix {
141            bits,
142            marker: PhantomData,
143        }
144    }
145
146    /// Like `Self::new`, but creates numbers in the constant context.
147    pub const fn constant(bits: Bits) -> Self {
148        Fix {
149            bits,
150            marker: PhantomData,
151        }
152    }
153
154    /// Converts to another _Exp_.
155    ///
156    /// # Examples
157    ///
158    /// ```
159    /// use fix::aliases::si::{Kilo, Milli};
160    /// let kilo = Kilo::new(5);
161    /// let milli = Milli::new(5_000_000);
162    /// assert_eq!(kilo, milli.convert());
163    /// assert_eq!(milli, kilo.convert());
164    /// ```
165    pub fn convert<ToExp>(self) -> Fix<Bits, Base, ToExp>
166    where
167        Bits: FromUnsigned + Pow + Mul<Output = Bits> + Div<Output = Bits>,
168        Base: Unsigned,
169        Exp: Sub<ToExp>,
170        Diff<Exp, ToExp>: Abs + IsLess<Z0>,
171        AbsVal<Diff<Exp, ToExp>>: Integer,
172    {
173        let base = Bits::from_unsigned::<Base>();
174        let diff = AbsVal::<Diff<Exp, ToExp>>::to_i32();
175        let inverse = Le::<Diff<Exp, ToExp>, Z0>::to_bool();
176
177        // FIXME: Would like to do this with typenum::Pow, but that
178        // seems to result in overflow evaluating requirements.
179        let ratio = base.pow(diff.unsigned_abs());
180
181        if inverse {
182            Fix::new(self.bits / ratio)
183        } else {
184            Fix::new(self.bits * ratio)
185        }
186    }
187
188    /// Converts the underlying bits to a wider type.
189    ///
190    /// # Examples
191    ///
192    /// ```
193    /// use fix::aliases::si::Milli;
194    /// let one = Milli::new(16899u64);
195    /// let mapped = one.widen::<u128>();
196    /// assert_eq!(mapped, Milli::new(16899u128));
197    /// ```
198    ///
199    pub fn widen<ToBits>(self) -> Fix<ToBits, Base, Exp>
200    where
201        ToBits: From<Bits>,
202    {
203        Fix::<ToBits, Base, Exp>::new(self.bits.into())
204    }
205
206    /// Attempts to converts underlying bits to a narrower type.
207    /// Returns `None` if conversion fails.
208    ///
209    /// # Examples
210    ///
211    /// ```
212    /// use fix::aliases::si::Milli;
213    /// let one = Milli::new(16899u128);
214    /// let mapped = one.narrow::<u64>();
215    /// assert_eq!(mapped, Some(Milli::new(16899u64)));
216    /// ```
217    ///
218    pub fn narrow<ToBits>(self) -> Option<Fix<ToBits, Base, Exp>>
219    where
220        ToBits: TryFrom<Bits>,
221    {
222        self.bits.try_into().ok().map(Fix::<ToBits, Base, Exp>::new)
223    }
224}
225
226/// Conversion from type-level [`Unsigned`] integers.
227///
228/// Enables being generic over types which can be created from type-level integers. It should
229/// probably be in `typenum` itself...
230///
231/// [`Unsigned`]: ../typenum/marker_traits/trait.Unsigned.html
232pub trait FromUnsigned {
233    /// Creates a value from a type.
234    fn from_unsigned<U>() -> Self
235    where
236        U: Unsigned;
237}
238
239macro_rules! impl_from_unsigned {
240    ($ty:ident) => {
241        impl FromUnsigned for $ty {
242            fn from_unsigned<U: Unsigned>() -> Self {
243                paste! { U::[<to_$ty>]() }
244            }
245        }
246    };
247}
248
249impl_from_unsigned!(u8);
250impl_from_unsigned!(u16);
251impl_from_unsigned!(u32);
252impl_from_unsigned!(u64);
253impl_from_unsigned!(u128);
254impl_from_unsigned!(usize);
255impl_from_unsigned!(i8);
256impl_from_unsigned!(i16);
257impl_from_unsigned!(i32);
258impl_from_unsigned!(i64);
259impl_from_unsigned!(i128);
260impl_from_unsigned!(isize);
261
262/// Exponentiation.
263///
264/// Enables being generic over integers which can be exponentiated. Why must we do this, standard
265/// library?
266pub trait Pow {
267    /// Raises `self` to the power of `exp`.
268    #[must_use]
269    fn pow(self, exp: u32) -> Self;
270}
271
272macro_rules! impl_pow {
273    ($ty:ident) => {
274        impl Pow for $ty {
275            #[inline]
276            fn pow(self, exp: u32) -> Self {
277                self.pow(exp)
278            }
279        }
280    };
281}
282
283impl_pow!(u8);
284impl_pow!(u16);
285impl_pow!(u32);
286impl_pow!(u64);
287impl_pow!(u128);
288impl_pow!(usize);
289impl_pow!(i8);
290impl_pow!(i16);
291impl_pow!(i32);
292impl_pow!(i64);
293impl_pow!(i128);
294impl_pow!(isize);
295
296// The usual traits.
297
298impl<Bits, Base, Exp> Copy for Fix<Bits, Base, Exp> where Bits: Copy {}
299
300impl<Bits, Base, Exp> Clone for Fix<Bits, Base, Exp>
301where
302    Bits: Clone,
303{
304    fn clone(&self) -> Self {
305        Self::new(self.bits.clone())
306    }
307}
308
309impl<Bits, Base, Exp> Default for Fix<Bits, Base, Exp>
310where
311    Bits: Default,
312{
313    fn default() -> Self {
314        Self::new(Bits::default())
315    }
316}
317
318impl<Bits, Base, Exp> Fix<Bits, Base, Exp>
319where
320    Bits: ConstZero,
321{
322    #[must_use]
323    pub const fn zero() -> Self {
324        Self::constant(Bits::ZERO)
325    }
326}
327
328impl<Bits, Base, Exp> Hash for Fix<Bits, Base, Exp>
329where
330    Bits: Hash,
331{
332    fn hash<H>(&self, state: &mut H)
333    where
334        H: Hasher,
335    {
336        self.bits.hash(state);
337    }
338}
339
340impl<Bits, Base, Exp> Debug for Fix<Bits, Base, Exp>
341where
342    Bits: Debug,
343    Base: Unsigned,
344    Exp: Integer,
345{
346    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
347        write!(f, "{:?}x{}^{}", self.bits, Base::to_u64(), Exp::to_i64())
348    }
349}
350
351impl<Bits, Exp> Display for Fix<Bits, U10, Exp>
352where
353    Bits: Display,
354    Exp: Integer,
355{
356    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
357        let exp = Exp::to_i32();
358        let decimals = usize::try_from(exp.unsigned_abs()).map_err(|_| Error)?;
359        let raw = self.bits.to_string();
360        let (sign, digits) = raw
361            .strip_prefix('-')
362            .map_or(("", raw.as_str()), |d| ("-", d));
363
364        match exp {
365            0.. => write!(f, "{sign}{digits}{}", "0".repeat(decimals)),
366            _ if digits.len() > decimals => {
367                let (integer, fraction) = digits.split_at(digits.len() - decimals);
368                write!(f, "{sign}{integer}.{fraction}")
369            }
370            _ => {
371                let padding = "0".repeat(decimals - digits.len());
372                write!(f, "{sign}0.{padding}{digits}")
373            }
374        }
375    }
376}
377
378// Comparison.
379
380impl<Bits, Base, Exp> Eq for Fix<Bits, Base, Exp> where Bits: Eq {}
381impl<Bits, Base, Exp> PartialEq for Fix<Bits, Base, Exp>
382where
383    Bits: PartialEq,
384{
385    fn eq(&self, rhs: &Self) -> bool {
386        self.bits == rhs.bits
387    }
388}
389
390impl<Bits, Base, Exp> PartialOrd for Fix<Bits, Base, Exp>
391where
392    Bits: PartialOrd,
393{
394    fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
395        self.bits.partial_cmp(&rhs.bits)
396    }
397}
398
399impl<Bits, Base, Exp> Ord for Fix<Bits, Base, Exp>
400where
401    Bits: Ord,
402{
403    fn cmp(&self, rhs: &Self) -> Ordering {
404        self.bits.cmp(&rhs.bits)
405    }
406}
407
408// Arithmetic.
409
410impl<Bits, Base, Exp> Neg for Fix<Bits, Base, Exp>
411where
412    Bits: Neg<Output = Bits>,
413{
414    type Output = Self;
415    fn neg(self) -> Self {
416        Self::new(-self.bits)
417    }
418}
419
420impl<Bits, Base, Exp> Add for Fix<Bits, Base, Exp>
421where
422    Bits: Add<Output = Bits>,
423{
424    type Output = Self;
425    fn add(self, rhs: Self) -> Self {
426        Self::new(self.bits + rhs.bits)
427    }
428}
429
430impl<Bits, Base, Exp> Sub for Fix<Bits, Base, Exp>
431where
432    Bits: Sub<Output = Bits>,
433{
434    type Output = Self;
435    fn sub(self, rhs: Self) -> Self {
436        Self::new(self.bits - rhs.bits)
437    }
438}
439
440impl<Bits, Base, LExp, RExp> Mul<Fix<Bits, Base, RExp>> for Fix<Bits, Base, LExp>
441where
442    Bits: Mul<Output = Bits>,
443    LExp: Add<RExp>,
444{
445    type Output = Fix<Bits, Base, Sum<LExp, RExp>>;
446    fn mul(self, rhs: Fix<Bits, Base, RExp>) -> Self::Output {
447        Self::Output::new(self.bits * rhs.bits)
448    }
449}
450
451impl<Bits, Base, LExp, RExp> Div<Fix<Bits, Base, RExp>> for Fix<Bits, Base, LExp>
452where
453    Bits: Div<Output = Bits>,
454    LExp: Sub<RExp>,
455{
456    type Output = Fix<Bits, Base, Diff<LExp, RExp>>;
457    fn div(self, rhs: Fix<Bits, Base, RExp>) -> Self::Output {
458        Self::Output::new(self.bits / rhs.bits)
459    }
460}
461
462impl<Bits, Base, Exp> Rem for Fix<Bits, Base, Exp>
463where
464    Bits: Rem<Output = Bits>,
465{
466    type Output = Self;
467    fn rem(self, rhs: Self) -> Self {
468        Self::new(self.bits % rhs.bits)
469    }
470}
471
472impl<Bits, Base, Exp> Mul<Bits> for Fix<Bits, Base, Exp>
473where
474    Bits: Mul<Output = Bits>,
475{
476    type Output = Self;
477    fn mul(self, rhs: Bits) -> Self {
478        Self::new(self.bits * rhs)
479    }
480}
481
482impl<Bits, Base, Exp> Div<Bits> for Fix<Bits, Base, Exp>
483where
484    Bits: Div<Output = Bits>,
485{
486    type Output = Self;
487    fn div(self, rhs: Bits) -> Self {
488        Self::new(self.bits / rhs)
489    }
490}
491
492impl<Bits, Base, Exp> Rem<Bits> for Fix<Bits, Base, Exp>
493where
494    Bits: Rem<Output = Bits>,
495{
496    type Output = Self;
497    fn rem(self, rhs: Bits) -> Self {
498        Self::new(self.bits % rhs)
499    }
500}
501
502impl<Bits, Base, Exp> AddAssign for Fix<Bits, Base, Exp>
503where
504    Bits: AddAssign,
505{
506    fn add_assign(&mut self, rhs: Self) {
507        self.bits += rhs.bits;
508    }
509}
510
511impl<Bits, Base, Exp> SubAssign for Fix<Bits, Base, Exp>
512where
513    Bits: SubAssign,
514{
515    fn sub_assign(&mut self, rhs: Self) {
516        self.bits -= rhs.bits;
517    }
518}
519
520impl<Bits, Base, Exp> MulAssign<Bits> for Fix<Bits, Base, Exp>
521where
522    Bits: MulAssign,
523{
524    fn mul_assign(&mut self, rhs: Bits) {
525        self.bits *= rhs;
526    }
527}
528
529impl<Bits, Base, Exp> DivAssign<Bits> for Fix<Bits, Base, Exp>
530where
531    Bits: DivAssign,
532{
533    fn div_assign(&mut self, rhs: Bits) {
534        self.bits /= rhs;
535    }
536}
537
538impl<Bits, Base, LExp, RExp> RemAssign<Fix<Bits, Base, RExp>> for Fix<Bits, Base, LExp>
539where
540    Bits: RemAssign,
541{
542    fn rem_assign(&mut self, rhs: Fix<Bits, Base, RExp>) {
543        self.bits %= rhs.bits;
544    }
545}
546
547impl<Bits, Base, Exp> RemAssign<Bits> for Fix<Bits, Base, Exp>
548where
549    Bits: RemAssign,
550{
551    fn rem_assign(&mut self, rhs: Bits) {
552        self.bits %= rhs;
553    }
554}
555
556// Checked arithmetic.
557
558impl<Bits, Base, Exp> CheckedAdd for Fix<Bits, Base, Exp>
559where
560    Bits: CheckedAdd,
561{
562    fn checked_add(&self, v: &Self) -> Option<Self> {
563        self.bits.checked_add(&v.bits).map(Self::new)
564    }
565}
566
567impl<Bits, Base, Exp> CheckedSub for Fix<Bits, Base, Exp>
568where
569    Bits: CheckedSub,
570{
571    fn checked_sub(&self, v: &Self) -> Option<Self> {
572        self.bits.checked_sub(&v.bits).map(Self::new)
573    }
574}
575
576impl<Bits, Base, Exp> Fix<Bits, Base, Exp>
577where
578    Self: CheckedSub,
579    Bits: Copy,
580{
581    #[must_use]
582    pub fn abs_diff(&self, v: &Self) -> Fix<Bits, Base, Exp> {
583        self.checked_sub(v).unwrap_or_else(|| *v - *self)
584    }
585}
586
587/// Adapts `CheckedMul` concept to this library with computed `Output` type.
588pub trait CheckedMulFix<Rhs> {
589    type Output;
590    fn checked_mul(&self, v: &Rhs) -> Option<Self::Output>;
591}
592
593impl<Bits, Base, LExp, RExp> CheckedMulFix<Fix<Bits, Base, RExp>> for Fix<Bits, Base, LExp>
594where
595    Bits: CheckedMul,
596    LExp: Add<RExp>,
597{
598    type Output = Fix<Bits, Base, Sum<LExp, RExp>>;
599    fn checked_mul(&self, v: &Fix<Bits, Base, RExp>) -> Option<Self::Output> {
600        self.bits.checked_mul(&v.bits).map(Self::Output::new)
601    }
602}
603
604/// Adapts `CheckedDiv` to this library with computed `Output` type.
605pub trait CheckedDivFix<Rhs> {
606    type Output;
607    fn checked_div(&self, v: &Rhs) -> Option<Self::Output>;
608}
609
610impl<Bits, Base, LExp, RExp> CheckedDivFix<Fix<Bits, Base, RExp>> for Fix<Bits, Base, LExp>
611where
612    Bits: CheckedDiv,
613    LExp: Sub<RExp>,
614{
615    type Output = Fix<Bits, Base, Diff<LExp, RExp>>;
616    fn checked_div(&self, v: &Fix<Bits, Base, RExp>) -> Option<Self::Output> {
617        self.bits.checked_div(&v.bits).map(Self::Output::new)
618    }
619}
620
621impl<Bits, Base, LExp, RExp> MulDiv<Fix<Bits, Base, RExp>> for Fix<Bits, Base, LExp>
622where
623    Bits: MulDiv,
624{
625    type Output = Fix<<Bits as MulDiv>::Output, Base, LExp>;
626    fn mul_div_ceil(
627        self,
628        num: Fix<Bits, Base, RExp>,
629        denom: Fix<Bits, Base, RExp>,
630    ) -> Option<Self::Output> {
631        self.bits
632            .mul_div_ceil(num.bits, denom.bits)
633            .map(Self::Output::new)
634    }
635    fn mul_div_floor(
636        self,
637        num: Fix<Bits, Base, RExp>,
638        denom: Fix<Bits, Base, RExp>,
639    ) -> Option<Self::Output> {
640        self.bits
641            .mul_div_floor(num.bits, denom.bits)
642            .map(Self::Output::new)
643    }
644    fn mul_div_round(
645        self,
646        num: Fix<Bits, Base, RExp>,
647        denom: Fix<Bits, Base, RExp>,
648    ) -> Option<Self::Output> {
649        self.bits
650            .mul_div_round(num.bits, denom.bits)
651            .map(Self::Output::new)
652    }
653}
654
655// Saturating arithmetic.
656
657impl<Bits, Base, Exp> SaturatingAdd for Fix<Bits, Base, Exp>
658where
659    Bits: SaturatingAdd,
660{
661    fn saturating_add(&self, v: &Self) -> Self {
662        Self::new(self.bits.saturating_add(&v.bits))
663    }
664}
665
666impl<Bits, Base, Exp> SaturatingSub for Fix<Bits, Base, Exp>
667where
668    Bits: SaturatingSub,
669{
670    fn saturating_sub(&self, v: &Self) -> Self {
671        Self::new(self.bits.saturating_sub(&v.bits))
672    }
673}
674
675#[cfg(test)]
676mod tests {
677    use num_traits::{SaturatingAdd, SaturatingSub};
678    use typenum::{N3, P3, Z0};
679
680    use crate::aliases::decimal::{IFix64, UFix64};
681    use crate::aliases::si::{Kilo, Micro, Milli, Nano, Unit};
682    use crate::{CheckedAdd, CheckedDivFix, CheckedMulFix, CheckedSub, MulDiv};
683
684    #[test]
685    fn convert_milli_to_kilo() {
686        assert_eq!(Kilo::new(15), Milli::new(15_000_000).convert());
687    }
688
689    #[test]
690    fn convert_kilo_to_milli() {
691        assert_eq!(Milli::new(15_000_000), Kilo::new(15).convert());
692    }
693
694    #[test]
695    fn cmp() {
696        assert!(Kilo::new(1) < Kilo::new(2));
697    }
698
699    #[test]
700    fn neg() {
701        assert_eq!(Kilo::new(-1), -Kilo::new(1i32));
702    }
703
704    #[test]
705    fn add() {
706        assert_eq!(Kilo::new(3), Kilo::new(1) + Kilo::new(2));
707    }
708
709    #[test]
710    fn sub() {
711        assert_eq!(Kilo::new(1), Kilo::new(3) - Kilo::new(2));
712    }
713
714    #[test]
715    fn mul() {
716        assert_eq!(Unit::new(6), Kilo::new(2) * Milli::new(3));
717    }
718
719    #[test]
720    fn div() {
721        assert_eq!(Unit::new(3), Kilo::new(6) / Kilo::new(2));
722    }
723
724    #[test]
725    fn rem() {
726        assert_eq!(Kilo::new(1), Kilo::new(6) % Kilo::new(5));
727    }
728
729    #[test]
730    fn mul_bits() {
731        assert_eq!(Kilo::new(6), Kilo::new(2) * 3);
732    }
733
734    #[test]
735    fn div_bits() {
736        assert_eq!(Kilo::new(3), Kilo::new(6) / 2);
737    }
738
739    #[test]
740    fn rem_bits() {
741        assert_eq!(Kilo::new(1), Kilo::new(6) % 5);
742    }
743
744    #[test]
745    fn add_assign() {
746        let mut a = Kilo::new(1);
747        a += Kilo::new(2);
748        assert_eq!(Kilo::new(3), a);
749    }
750
751    #[test]
752    fn sub_assign() {
753        let mut a = Kilo::new(3);
754        a -= Kilo::new(2);
755        assert_eq!(Kilo::new(1), a);
756    }
757
758    #[test]
759    fn mul_assign_bits() {
760        let mut a = Kilo::new(2);
761        a *= 3;
762        assert_eq!(Kilo::new(6), a);
763    }
764
765    #[test]
766    fn div_assign_bits() {
767        let mut a = Kilo::new(6);
768        a /= 2;
769        assert_eq!(Kilo::new(3), a);
770    }
771
772    #[test]
773    fn rem_assign() {
774        let mut a = Kilo::new(6);
775        a %= Milli::new(5);
776        assert_eq!(Kilo::new(1), a);
777    }
778
779    #[test]
780    fn rem_assign_bits() {
781        let mut a = Kilo::new(6);
782        a %= 5;
783        assert_eq!(Kilo::new(1), a);
784    }
785
786    #[test]
787    fn checked_add_neg() {
788        let max = Kilo::new(u8::MAX);
789        let one = Kilo::new(1);
790        assert!(max.checked_add(&one).is_none());
791    }
792
793    #[test]
794    fn checked_add_pos() {
795        let forty = Kilo::new(40);
796        let two = Kilo::new(2);
797        assert_eq!(forty.checked_add(&two), Some(Kilo::new(42)));
798    }
799
800    #[test]
801    fn checked_sub_neg() {
802        let one = Kilo::new(1);
803        let max = Kilo::new(u8::MAX);
804        assert!(one.checked_sub(&max).is_none());
805    }
806
807    #[test]
808    fn checked_sub_pos() {
809        let fifty = Kilo::new(50);
810        let eight = Kilo::new(8);
811        assert_eq!(fifty.checked_sub(&eight), Some(Kilo::new(42)));
812    }
813
814    #[test]
815    fn checked_mul_neg() {
816        let fifty = Kilo::new(50);
817        let max = Kilo::new(u8::MAX);
818        assert!(fifty.checked_mul(&max).is_none());
819    }
820
821    #[test]
822    fn checked_mul_pos() {
823        let fifty = Kilo::new(50_u64);
824        assert_eq!(
825            fifty.checked_mul(&fifty).map(super::Fix::convert),
826            Some(Kilo::new(2_500_000_u64))
827        );
828    }
829
830    #[test]
831    fn checked_div_neg() {
832        let one = Unit::new(0);
833        assert!(one.checked_div(&one).is_none());
834    }
835
836    #[test]
837    fn checked_div_pos() {
838        let hundred = Kilo::new(100);
839        let five = Kilo::new(5);
840        assert_eq!(hundred.checked_div(&five), Some(Unit::new(20)));
841    }
842
843    #[test]
844    fn narrow_succeeds() {
845        let one = Milli::new(1000u128);
846        let mapped = one.narrow::<u64>();
847        assert_eq!(mapped, Some(Milli::new(1000u64)));
848    }
849
850    #[test]
851    fn narrow_fails() {
852        let one = Milli::new(1699u64);
853        let mapped = one.narrow::<u8>();
854        assert_eq!(mapped, None);
855    }
856
857    #[test]
858    fn widen_succeeds() {
859        let one = Milli::new(1_340_191u64);
860        let mapped = one.widen::<u128>();
861        assert_eq!(mapped, Milli::new(1_340_191_u128));
862    }
863
864    #[test]
865    fn mul_div_ceil() {
866        let start = Milli::new(313_459u64);
867        let mul = Milli::new(1200u64);
868        let div = Milli::new(2450u64);
869        assert_eq!(start.mul_div_ceil(mul, div), Some(Milli::new(153_531)));
870    }
871
872    #[test]
873    fn mul_div_ceil_unit() {
874        let start = Milli::new(31_345_934u64);
875        let mul = Milli::new(1000u64);
876        let div = Milli::new(2000u64);
877        assert_eq!(
878            start.mul_div_ceil(mul, div),
879            Some(Milli::new(15_672_967_u64))
880        );
881    }
882
883    #[test]
884    fn mul_div_floor() {
885        let start = Milli::new(69_693u64);
886        let mul = Milli::new(5_192u64);
887        let div = Milli::new(190u64);
888        assert_eq!(
889            start.mul_div_floor(mul, div),
890            Some(Milli::new(1_904_452_u64))
891        );
892    }
893
894    #[test]
895    fn mul_div_floor_unit() {
896        let start = Milli::new(69_693u64);
897        let mul = Milli::new(1000u64);
898        let div = Milli::new(9u64);
899        assert_eq!(
900            start.mul_div_floor(mul, div),
901            Some(Milli::new(7_743_666_u64))
902        );
903    }
904
905    #[test]
906    fn mul_div_round() {
907        let start = Milli::new(1892u64);
908        let mul = Milli::new(3222u64);
909        let div = Milli::new(9999u64);
910        assert_eq!(start.mul_div_round(mul, div), Some(Milli::new(610u64)));
911    }
912
913    #[test]
914    fn mul_div_round_unit() {
915        let start = Milli::new(1892u64);
916        let mul = Milli::new(1000u64);
917        let div = Milli::new(322u64);
918        assert_eq!(start.mul_div_round(mul, div), Some(Milli::new(5876u64)));
919    }
920
921    #[test]
922    fn abs_diff() {
923        let start = Milli::new(u128::MIN);
924        let end = Milli::new(u128::MAX);
925        assert_eq!(start.abs_diff(&end), end);
926    }
927
928    #[test]
929    fn constant() {
930        assert_eq!(Kilo::constant(69u64), Kilo::new(69u64));
931    }
932
933    #[test]
934    fn saturating_sub() {
935        let zero = Kilo::constant(0);
936        let result = zero.saturating_sub(&Kilo::new(69u64));
937        assert_eq!(zero, result);
938    }
939
940    #[test]
941    fn saturating_add() {
942        let max = Kilo::new(u64::MAX);
943        let result = max.saturating_add(&Kilo::new(69u64));
944        assert_eq!(max, result);
945    }
946
947    #[test]
948    fn zero_is_zero() {
949        assert_eq!(Kilo::<u64>::zero().bits, 0);
950        assert_eq!(Milli::<u64>::zero().bits, 0);
951        assert_eq!(Nano::<u64>::zero().bits, 0);
952    }
953
954    #[test]
955    fn one_is_correct() {
956        assert_eq!(Milli::<u64>::one().bits, 1_000);
957        assert_eq!(Micro::<u64>::one().bits, 1_000_000);
958        assert_eq!(Nano::<u64>::one().bits, 1_000_000_000);
959    }
960
961    #[test]
962    fn checked_convert_upconvert() {
963        assert_eq!(
964            Milli::new(5u64).checked_convert(),
965            Some(Micro::new(5_000u64)),
966        );
967    }
968
969    #[test]
970    fn checked_convert_downconvert() {
971        assert_eq!(
972            Micro::new(5_000u64).checked_convert(),
973            Some(Milli::new(5u64)),
974        );
975    }
976
977    #[test]
978    fn checked_convert_identity() {
979        assert_eq!(Milli::new(42u64).checked_convert(), Some(Milli::new(42u64)),);
980    }
981
982    #[test]
983    fn checked_convert_overflow() {
984        assert_eq!(Milli::new(u64::MAX).checked_convert::<typenum::N9>(), None,);
985    }
986
987    #[test]
988    fn checked_convert_matches_convert() {
989        let value = Milli::new(15u64);
990        assert_eq!(
991            value.checked_convert::<typenum::N6>(),
992            Some(value.convert::<typenum::N6>()),
993        );
994    }
995
996    #[test]
997    fn checked_convert_ceil_downconvert_rounds_up() {
998        assert_eq!(
999            Micro::new(5_001u64).checked_convert_ceil(),
1000            Some(Milli::new(6u64)),
1001        );
1002    }
1003
1004    #[test]
1005    fn checked_convert_ceil_exact_matches_floor() {
1006        assert_eq!(
1007            Micro::new(5_000u64).checked_convert_ceil(),
1008            Some(Milli::new(5u64)),
1009        );
1010    }
1011
1012    #[test]
1013    fn checked_convert_ceil_upconvert() {
1014        assert_eq!(
1015            Milli::new(5u64).checked_convert_ceil(),
1016            Some(Micro::new(5_000u64)),
1017        );
1018    }
1019
1020    #[test]
1021    fn checked_convert_ceil_overflow() {
1022        assert_eq!(
1023            Milli::new(u64::MAX).checked_convert_ceil::<typenum::N9>(),
1024            None,
1025        );
1026    }
1027
1028    #[test]
1029    fn display_negative_exp() {
1030        assert_eq!(UFix64::<N3>::new(1_234).to_string(), "1.234");
1031        assert_eq!(UFix64::<N3>::new(1).to_string(), "0.001");
1032        assert_eq!(UFix64::<N3>::new(0).to_string(), "0.000");
1033    }
1034
1035    #[test]
1036    fn display_negative_exp_signed() {
1037        assert_eq!(IFix64::<N3>::new(-1_234).to_string(), "-1.234");
1038        assert_eq!(IFix64::<N3>::new(-1).to_string(), "-0.001");
1039    }
1040
1041    #[test]
1042    fn display_positive_exp() {
1043        assert_eq!(UFix64::<P3>::new(5).to_string(), "5000");
1044    }
1045
1046    #[test]
1047    fn display_zero_exp() {
1048        assert_eq!(UFix64::<Z0>::new(42).to_string(), "42");
1049    }
1050}