rust_fixed_point_decimal/
lib.rs

1// ---------------------------------------------------------------------------
2// Copyright:   (c) 2021 ff. Michael Amrhein (michael@adrhinum.de)
3// License:     This program is part of a larger application. For license
4//              details please read the file LICENSE.TXT provided together
5//              with the application.
6// ---------------------------------------------------------------------------
7// $Source: src/lib.rs $
8// $Revision: 2021-10-29T16:55:20+02:00 $
9
10#![doc = include_str!("../README.md")]
11#![allow(dead_code)]
12#![allow(incomplete_features)]
13#![feature(generic_const_exprs)]
14#![feature(associated_type_bounds)]
15#![feature(int_roundings)]
16#![warn(missing_docs)]
17
18#[doc(inline)]
19pub use binops::{div_rounded::DivRounded, mul_rounded::MulRounded};
20#[doc(inline)]
21pub use errors::*;
22#[doc(inline)]
23pub use rounding::{Round, RoundInto, RoundingMode};
24#[doc(inline)]
25pub use rust_fixed_point_decimal_core::{ParseDecimalError, MAX_PREC};
26#[doc(inline)]
27pub use rust_fixed_point_decimal_macros::Dec;
28
29use crate::prec_constraints::{PrecLimitCheck, True};
30
31mod binops;
32mod errors;
33mod format;
34mod from_decimal;
35mod from_float;
36mod from_int;
37mod from_str;
38mod rounding;
39mod unops;
40
41mod prec_constraints {
42    pub trait True {}
43
44    pub struct PrecLimitCheck<const CHECK: bool> {}
45
46    impl True for PrecLimitCheck<true> {}
47}
48
49/// Represents a decimal number as a coefficient (stored as an `i128` value)
50/// combined with a type parameter specifying the number of fractional decimal
51/// digits.
52///
53/// The type parameter `P` can be in the range 0 .. [`MAX_PREC`].
54#[derive(Copy, Clone, Eq, Ord)]
55#[repr(transparent)]
56pub struct Decimal<const P: u8>
57where
58    // Decimal<P>: Sized,
59    PrecLimitCheck<{ P <= MAX_PREC }>: True,
60{
61    coeff: i128,
62}
63
64impl<const P: u8> Decimal<P>
65where
66    PrecLimitCheck<{ P <= MAX_PREC }>: True,
67{
68    // needs to be public because of macro Dec!
69    #[doc(hidden)]
70    #[inline(always)]
71    pub fn new_raw(val: i128) -> Self {
72        Self { coeff: val }
73    }
74
75    /// Internal representation. For debugging only!
76    #[doc(hidden)]
77    #[inline(always)]
78    pub fn coefficient(self) -> i128 {
79        self.coeff
80    }
81
82    /// Number of fractional decimal digits
83    #[inline(always)]
84    pub const fn precision(self) -> u8 {
85        P
86    }
87
88    /// Additive identity
89    pub const ZERO: Decimal<P> = Decimal { coeff: 0i128 };
90    /// Multiplicative identity
91    pub const ONE: Decimal<P> = Decimal {
92        coeff: 10i128.pow(P as u32),
93    };
94    /// Multiplicative negator
95    pub const NEG_ONE: Decimal<P> = Decimal {
96        coeff: -(10i128.pow(P as u32)),
97    };
98    /// Equivalent of 2
99    pub const TWO: Decimal<P> = Decimal {
100        coeff: 2i128 * 10i128.pow(P as u32),
101    };
102    /// Equivalent of 10
103    pub const TEN: Decimal<P> = Decimal {
104        coeff: 10i128.pow((P + 1) as u32),
105    };
106    /// Maximum value representable by this type
107    pub const MAX: Decimal<P> = Decimal { coeff: i128::MAX };
108    /// Minimum value representable by this type
109    pub const MIN: Decimal<P> = Decimal { coeff: i128::MIN };
110    /// Smallest absolute difference between two non-equal values of this type
111    pub const DELTA: Decimal<P> = Decimal { coeff: 1i128 };
112}
113
114impl<const P: u8> Default for Decimal<P>
115where
116    PrecLimitCheck<{ P <= MAX_PREC }>: True,
117{
118    /// Default value: Decimal::\<P\>::ZERO
119    #[inline(always)]
120    fn default() -> Self {
121        Self::ZERO
122    }
123}
124
125#[cfg(test)]
126mod tests {
127    use crate::Decimal;
128
129    #[test]
130    fn test_new_raw() {
131        let val = 12345678901234567890_i128;
132        let d: Decimal<5> = Decimal::new_raw(val);
133        assert_eq!(d.coeff, val);
134        assert_eq!(d.precision(), 5);
135        let d: Decimal<9> = Decimal::new_raw(val);
136        assert_eq!(d.coeff, val);
137        assert_eq!(d.precision(), 9);
138    }
139
140    macro_rules! test_constants_and_default {
141        () => {test_constants_and_default!(0,1,2,7,9);};
142        ($($p:expr),*) => {
143            #[test]
144            fn test_consts() {
145            $(
146                assert_eq!(Decimal::<$p>::ZERO.coeff, 0i128);
147                assert_eq!(Decimal::<$p>::default().coeff, 0i128);
148                assert_eq!(Decimal::<$p>::ONE.coeff, 10i128.pow($p));
149                assert_eq!(Decimal::<$p>::NEG_ONE.coeff,
150                           Decimal::<$p>::ONE.coeff.checked_neg().unwrap());
151                assert_eq!(Decimal::<$p>::TWO.coeff,
152                           Decimal::<$p>::ONE.coeff * 2);
153                assert_eq!(Decimal::<$p>::TEN.coeff, 10i128.pow($p + 1));
154                assert_eq!(Decimal::<$p>::MAX.coeff, i128::MAX);
155                assert_eq!(Decimal::<$p>::MIN.coeff, i128::MIN);
156                assert_eq!(Decimal::<$p>::DELTA.coeff, 1i128);
157            )*
158            }
159        }
160    }
161
162    test_constants_and_default!();
163}