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