Skip to main content

decimax/
lib.rs

1#![no_std]
2#![doc = include_str!("../README.md")]
3
4mod convert;
5mod display;
6mod from_str;
7mod ops;
8
9mod int128;
10mod int32;
11mod int64;
12
13use core::fmt::{Debug, Display};
14use core::ops::{Add, AddAssign, BitAnd, BitOr, BitXor, Div, Mul, Rem, Shl, Shr, Sub, SubAssign};
15
16pub use from_str::ParseError;
17
18/// The 128-bit decimal type, with about 36 significant digits and at most 31 fraction digits.
19pub type Dec128 = Decimal<u128, true>;
20
21/// The 128-bit unsigned decimal type, with about 36 significant digits and at most 31 fraction digits.
22///
23/// Since cargo generates full documentation only for the first alias ([`Dec128`]),
24/// here is almost empty. See the [`Decimal`] page for full documentation of this alias.
25pub type UDec128 = Decimal<u128, false>;
26
27/// The 64-bit decimal type, with about 18 significant digits and at most 15 fraction digits.
28///
29/// Since cargo generates full documentation only for the first alias ([`Dec128`]),
30/// here is almost empty. See the [`Decimal`] page for full documentation of this alias.
31pub type Dec64 = Decimal<u64, true>;
32
33/// The 64-bit unsigned decimal type, with about 18 significant digits and at most 15 fraction digits.
34///
35/// Since cargo generates full documentation only for the first alias ([`Dec128`]),
36/// here is almost empty. See the [`Decimal`] page for full documentation of this alias.
37pub type UDec64 = Decimal<u64, false>;
38
39/// The 32-bit decimal type, with about 9 significant digits and at most 7 fraction digits.
40///
41/// Since cargo generates full documentation only for the first alias ([`Dec128`]),
42/// here is almost empty. See the [`Decimal`] page for full documentation of this alias.
43pub type Dec32 = Decimal<u32, true>;
44
45/// The 32-bit unsigned decimal type, with about 9 significant digits and at most 7 fraction digits.
46///
47/// Since cargo generates full documentation only for the first alias ([`Dec128`]),
48/// here is almost empty. See the [`Decimal`] page for full documentation of this alias.
49pub type UDec32 = Decimal<u32, false>;
50
51/// The decimal type.
52///
53/// The `I` is the underlying integer type, which could be `u128`, `u64` or `u32`.
54///
55/// The `S` is for signed or unsigned.
56///
57/// They have aliases [`Dec128`], [`Dec64`], [`Dec32`], [`UDec128`], [`UDec64`]
58/// and [`UDec32`] respectively.
59#[derive(Copy, Clone, Default)]
60#[repr(transparent)]
61pub struct Decimal<I: UnderlyingInt, const S: bool>(I);
62
63/// Underlying integer type.
64pub trait UnderlyingInt:
65    Sized
66    + Clone
67    + Copy
68    + Debug
69    + Display
70    + PartialEq
71    + Eq
72    + PartialOrd
73    + Ord
74    + Add<Output = Self>
75    + Sub<Output = Self>
76    + Div<Output = Self>
77    + Rem<Output = Self>
78    + Mul<Output = Self>
79    + Shl<u32, Output = Self>
80    + Shr<u32, Output = Self>
81    + BitOr<Output = Self>
82    + BitAnd<Output = Self>
83    + BitXor<Output = Self>
84    + AddAssign
85    + SubAssign
86{
87    const MAX: Self;
88    const ZERO: Self;
89    const ONE: Self;
90    const TEN: Self;
91    const HUNDRED: Self;
92
93    const UNSIGNED_MAX_MATISSA: Self;
94    const SIGNED_MAX_MATISSA: Self;
95    const SIGNED_MIN_UNDERINT: Self;
96
97    const BITS: u32;
98    const SCALE_BITS: u32;
99    const MAX_SCALE: u32 = 2_u32.pow(Self::SCALE_BITS) - 1;
100
101    type Signed: Display;
102
103    // to pack and unpack
104    fn to_signed(self, sign: u8) -> Self::Signed;
105    fn from_signed(s: Self::Signed) -> (Self, u8);
106
107    // to decode and encode the scale
108    fn as_u32(self) -> u32;
109    fn from_u32(n: u32) -> Self;
110
111    fn leading_zeros(self) -> u32;
112
113    // caller has made sure that no overflow
114    fn mul_exp(self, iexp: u32) -> Self;
115
116    // the implementation need to check if overflow
117    fn checked_mul_exp(self, iexp: u32) -> Option<Self>;
118
119    // caller has made sure that @iexp is in range
120    // remember to round the result
121    fn div_exp(self, iexp: u32) -> Self;
122
123    // caller has made sure that @iexp is in range
124    fn div_rem_exp(self, iexp: u32) -> (Self, Self);
125
126    // calculate `self * right` with sum of scales
127    fn mul_with_sum_scale<const S: bool>(self, right: Self, sum_scale: u32) -> Option<(Self, u32)>;
128
129    // calculate `self / right` with scales
130    fn div_with_scales<const S: bool>(
131        self,
132        d: Self,
133        s_scale: u32,
134        d_scale: u32,
135    ) -> Option<(Self, u32)>;
136}
137
138// for signed and unsigned both
139impl<I: UnderlyingInt, const S: bool> Decimal<I, S> {
140    /// Zero.
141    pub const ZERO: Self = Self(I::ZERO);
142
143    /// One.
144    pub const ONE: Self = Self(I::ONE);
145
146    const META_BITS: u32 = (S as u32) + I::SCALE_BITS;
147    const MANTISSA_BITS: u32 = I::BITS - Self::META_BITS;
148
149    // layout:
150    //   +-+-----+-------------+
151    //   |S|scale|  mantissa   |
152    //   +-+-----+-------------+
153    fn unpack(self) -> (u8, u32, I) {
154        if S {
155            let mantissa = self.0 & I::SIGNED_MAX_MATISSA;
156            let meta = (self.0 >> Self::MANTISSA_BITS).as_u32();
157            let scale = meta & ((1 << I::SCALE_BITS) - 1);
158            let sign = meta >> I::SCALE_BITS;
159            (sign as u8, scale, mantissa)
160        } else {
161            let mantissa = self.0 & I::UNSIGNED_MAX_MATISSA;
162            let scale = (self.0 >> Self::MANTISSA_BITS).as_u32();
163            (0, scale, mantissa)
164        }
165    }
166
167    // the caller must make sure that the inputs are valid
168    fn pack(sign: u8, scale: u32, mantissa: I) -> Self {
169        let meta = if S {
170            debug_assert!(sign <= 1);
171            debug_assert!(scale <= I::MAX_SCALE);
172            debug_assert!(Self::valid_mantissa(mantissa));
173
174            ((sign as u32) << I::SCALE_BITS) | scale
175        } else {
176            debug_assert!(sign == 0);
177            debug_assert!(scale <= I::MAX_SCALE);
178            debug_assert!(Self::valid_mantissa(mantissa));
179
180            scale
181        };
182
183        Self(I::from_u32(meta) << Self::MANTISSA_BITS | mantissa)
184    }
185
186    fn valid_mantissa(man: I) -> bool {
187        man <= (I::MAX >> Self::META_BITS)
188    }
189
190    /// Return the underlying integer.
191    ///
192    /// You should not try to decode this integer yourself.
193    /// You should just hold this and load it later.
194    pub const fn underlying(self) -> I {
195        self.0
196    }
197
198    /// Load from underlying integer, which should only be got from [`Self::underlying`].
199    pub const fn from_underlying(i: I) -> Self {
200        Self(i)
201    }
202}
203
204impl<I: UnderlyingInt> Decimal<I, true> {
205    /// The largest value.
206    pub const MAX: Self = Self(I::SIGNED_MAX_MATISSA);
207
208    /// The smallest value. It's the negative of [`Self::MAX`].
209    pub const MIN: Self = Self(I::SIGNED_MIN_UNDERINT);
210
211    /// Deconstruct the decimal into signed mantissa and scale.
212    ///
213    /// # Examples:
214    ///
215    /// ```
216    /// use decimax::Dec128;
217    /// use core::str::FromStr;
218    ///
219    /// let d = Dec128::from_str("3.14").unwrap();
220    /// assert_eq!(d.parts(), (314, 2));
221    /// ```
222    pub fn parts(self) -> (I::Signed, u32) {
223        let (sign, scale, man) = self.unpack();
224        (I::to_signed(man, sign), scale)
225    }
226
227    /// Construct a decimal from signed mantissa and scale.
228    ///
229    /// # Panic:
230    ///
231    /// Panic if the mantissa or scale is out of range. Use [`Self::try_from_parts`]
232    /// for the checked version.
233    ///
234    /// # Examples:
235    ///
236    /// ```
237    /// use decimax::Dec128;
238    /// let d = Dec128::from_parts(314, 2);
239    /// assert_eq!(d.to_string(), "3.14");
240    /// ```
241    pub fn from_parts<S>(mantissa: S, scale: u32) -> Self
242    where
243        S: Into<I::Signed>,
244    {
245        Self::try_from_parts(mantissa, scale).expect("invalid decimal input parts")
246    }
247
248    /// Construct a decimal from signed mantissa and scale.
249    ///
250    /// Return `None` if the mantissa or scale is out of range.
251    ///
252    /// # Examples:
253    ///
254    /// ```
255    /// use decimax::Dec128;
256    /// let d = Dec128::try_from_parts(314, 2).unwrap();
257    /// assert_eq!(d.to_string(), "3.14");
258    ///
259    /// let d = Dec128::try_from_parts(314, 99); // 99 is out of range
260    /// assert!(d.is_none());
261    /// ```
262    pub fn try_from_parts<S>(mantissa: S, scale: u32) -> Option<Self>
263    where
264        S: Into<I::Signed>,
265    {
266        if scale > I::MAX_SCALE {
267            return None;
268        }
269        let (man, sign) = I::from_signed(mantissa.into());
270        if man > I::SIGNED_MAX_MATISSA {
271            return None;
272        }
273        Some(Self::pack(sign, scale, man))
274    }
275}
276
277impl<I: UnderlyingInt> Decimal<I, false> {
278    /// The largest value of unsigned decimal.
279    pub const MAX: Self = Self(I::UNSIGNED_MAX_MATISSA);
280
281    /// The smallest value of unsigned decimal. It's zero.
282    pub const MIN: Self = Self::ZERO;
283
284    /// Deconstruct the unsigned decimal into mantissa and scale.
285    ///
286    /// # Examples:
287    ///
288    /// ```
289    /// use decimax::UDec128;
290    /// use core::str::FromStr;
291    ///
292    /// let d = UDec128::from_str("3.14").unwrap();
293    /// assert_eq!(d.parts(), (314, 2));
294    /// ```
295    pub fn parts(self) -> (I, u32) {
296        let (_, scale, man) = self.unpack();
297        (man, scale)
298    }
299
300    /// Construct a unsigned decimal from mantissa and scale.
301    ///
302    /// # Panic:
303    ///
304    /// Panic if the mantissa or scale is out of range. Use [`Self::try_from_parts`]
305    /// for the checked version.
306    ///
307    /// # Examples:
308    ///
309    /// ```
310    /// use decimax::UDec128;
311    /// let d = UDec128::from_parts(314, 2);
312    /// assert_eq!(d.to_string(), "3.14");
313    /// ```
314    pub fn from_parts(mantissa: I, scale: u32) -> Self {
315        Self::try_from_parts(mantissa, scale).expect("invalid decimal input parts")
316    }
317
318    /// Construct a unsigned decimal from signed mantissa and scale.
319    ///
320    /// Return `None` if the mantissa or scale is out of range.
321    ///
322    /// # Examples:
323    ///
324    /// ```
325    /// use decimax::UDec128;
326    /// let d = UDec128::try_from_parts(314, 2).unwrap();
327    /// assert_eq!(d.to_string(), "3.14");
328    ///
329    /// let d = UDec128::try_from_parts(314, 99); // 99 is out of range
330    /// assert!(d.is_none());
331    /// ```
332    pub fn try_from_parts(mantissa: I, scale: u32) -> Option<Self> {
333        if scale > I::MAX_SCALE {
334            return None;
335        }
336        if mantissa > I::UNSIGNED_MAX_MATISSA {
337            return None;
338        }
339        Some(Self::pack(0, scale, mantissa))
340    }
341}
342
343pub(crate) fn bits_to_digits(bits: u32) -> u32 {
344    bits * 1233 >> 12 // math!
345}