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