Skip to main content

fpdec/
unops.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/unops.rs $
8// $Revision: 2023-05-12T15:46:50+02:00 $
9
10use core::ops::Neg;
11
12use fpdec_core::ten_pow;
13
14use crate::Decimal;
15
16// TODO: remove this trait when feature(int_roundings) got stable
17trait DivModInt: Sized {
18    fn divmod(self, rhs: Self) -> (Self, Self);
19    fn div_floor(self, rhs: Self) -> Self;
20    fn div_ceil(self, rhs: Self) -> Self;
21}
22
23impl DivModInt for i128 {
24    #[allow(clippy::integer_division)]
25    #[inline(always)]
26    fn divmod(self, rhs: Self) -> (Self, Self) {
27        (self / rhs, self % rhs)
28    }
29    #[inline]
30    fn div_floor(self, rhs: Self) -> Self {
31        let (q, r) = self.divmod(rhs);
32        if (r > 0 && rhs < 0) || (r < 0 && rhs > 0) {
33            q - 1
34        } else {
35            q
36        }
37    }
38    #[inline]
39    fn div_ceil(self, rhs: Self) -> Self {
40        let (q, r) = self.divmod(rhs);
41        if (r > 0 && rhs > 0) || (r < 0 && rhs < 0) {
42            q + 1
43        } else {
44            q
45        }
46    }
47}
48
49impl Neg for Decimal {
50    type Output = Self;
51
52    /// Returns -self.
53    ///
54    /// # Panics
55    ///
56    /// Panics with 'attempt to negate with overflow' when called on a
57    /// `Decimal` with a coefficient equal to `i128::MIN`!
58    #[inline(always)]
59    fn neg(self) -> Self::Output {
60        Self::Output {
61            coeff: -self.coeff,
62            n_frac_digits: self.n_frac_digits,
63        }
64    }
65}
66
67impl Neg for &Decimal {
68    type Output = <Decimal as Neg>::Output;
69
70    /// Returns -self.
71    ///
72    /// #Panics
73    ///
74    /// Panics with 'attempt to negate with overflow' when called on a
75    /// `Decimal` with a coefficient equal to `i128::MIN`!
76    #[inline(always)]
77    fn neg(self) -> Self::Output {
78        Self::Output {
79            coeff: -self.coeff,
80            n_frac_digits: self.n_frac_digits,
81        }
82    }
83}
84
85impl Decimal {
86    /// Returns the absolute value of `self`.
87    #[inline(always)]
88    pub const fn abs(&self) -> Self {
89        Self {
90            coeff: self.coefficient().abs(),
91            n_frac_digits: self.n_frac_digits,
92        }
93    }
94
95    /// Returns the sign of the number.
96    ///
97    /// * `0` if the number is zero
98    /// * `1` if the number is positive
99    /// * `-1` if the number is negative
100    #[inline(always)]
101    pub fn signum(&self) -> Self {
102        Self::from(self.coefficient().signum())
103    }
104
105    /// Returns the largest integral value <= `self`.
106    ///
107    /// # Examples
108    ///
109    /// ```rust
110    /// # use fpdec::{Dec, Decimal};
111    /// let d = Dec!(17.5);
112    /// assert_eq!(d.floor().to_string(), "17");
113    /// let d = Dec!(-17.050);
114    /// assert_eq!(d.floor().to_string(), "-18");
115    /// ```
116    #[inline]
117    pub fn floor(&self) -> Self {
118        match self.n_frac_digits {
119            0 => *self,
120            n => Self {
121                // coeff: self.coeff.div_floor(ten_pow(n)),
122                coeff: DivModInt::div_floor(self.coeff, ten_pow(n)),
123                n_frac_digits: 0,
124            },
125        }
126    }
127
128    /// Returns the smallest integral value >= `self`.
129    ///
130    /// # Examples
131    ///
132    /// ```rust
133    /// # use fpdec::{Dec, Decimal};
134    /// let d = Dec!(17.5);
135    /// assert_eq!(d.ceil().to_string(), "18");
136    /// let d = Dec!(-17.50);
137    /// assert_eq!(d.ceil().to_string(), "-17");
138    /// ```
139    #[inline]
140    pub fn ceil(&self) -> Self {
141        match self.n_frac_digits {
142            0 => *self,
143            n => Self {
144                // coeff: self.coeff.div_ceil(ten_pow(n)),
145                coeff: DivModInt::div_ceil(self.coeff, ten_pow(n)),
146                n_frac_digits: 0,
147            },
148        }
149    }
150
151    /// Returns the integral part of `self`.
152    ///
153    /// # Examples
154    ///
155    /// ```rust
156    /// # use fpdec::{Dec, Decimal};
157    /// let d = Dec!(17.5);
158    /// assert_eq!(d.trunc().to_string(), "17");
159    /// let d = Dec!(-17.55555);
160    /// assert_eq!(d.trunc().to_string(), "-17");
161    /// ```
162    #[allow(clippy::integer_division)]
163    #[inline]
164    pub const fn trunc(&self) -> Self {
165        match self.n_frac_digits {
166            0 => *self,
167            n => Self {
168                coeff: self.coeff / ten_pow(n),
169                n_frac_digits: 0,
170            },
171        }
172    }
173
174    /// Returns the fractional part of `self`.
175    ///
176    /// # Examples
177    ///
178    /// ```rust
179    /// # use fpdec::{Dec, Decimal};
180    /// let d = Dec!(17.050);
181    /// assert_eq!(d.fract().to_string(), "0.050");
182    /// let d = Dec!(-17.5);
183    /// assert_eq!(d.fract().to_string(), "-0.5");
184    /// ```
185    #[inline]
186    pub const fn fract(&self) -> Self {
187        match self.n_frac_digits {
188            0 => Self::ZERO,
189            n => Self {
190                coeff: self.coeff % ten_pow(n),
191                n_frac_digits: n,
192            },
193        }
194    }
195}
196
197#[cfg(test)]
198mod tests {
199    use super::*;
200
201    #[test]
202    fn test_neg() {
203        let val = 1234567890_i128;
204        let x: Decimal = Decimal::new_raw(val, 2);
205        let y = -x;
206        assert_eq!(x.coefficient(), -y.coefficient());
207        let z = -y;
208        assert_eq!(x.coefficient(), z.coefficient());
209        let a = &x;
210        let b = -a;
211        assert_eq!(a.coefficient(), -b.coefficient());
212    }
213
214    #[test]
215    fn test_neg_corner_cases() {
216        let x = Decimal::MAX;
217        let y = Decimal::MIN;
218        assert_eq!(x, -y);
219        assert_eq!(-x, y);
220    }
221
222    #[test]
223    fn test_abs() {
224        let x = Decimal::new_raw(-123456789, 4);
225        let y = x.abs();
226        assert_eq!(-x.coefficient(), y.coefficient());
227        let z = y.abs();
228        assert_eq!(y.coefficient(), z.coefficient());
229        let a = &x;
230        let b = a.abs();
231        assert_eq!(-a.coefficient(), b.coefficient());
232    }
233
234    #[test]
235    fn test_signum() {
236        assert_eq!(Decimal::new_raw(123, 0).signum(), Decimal::ONE);
237        assert_eq!(Decimal::new_raw(0, 0).signum(), Decimal::ZERO);
238        assert_eq!(Decimal::new_raw(-123, 0).signum(), Decimal::NEG_ONE);
239    }
240
241    #[test]
242    fn test_floor() {
243        let x = Decimal::new_raw(123, 0);
244        let y = x.floor();
245        assert_eq!(y.n_frac_digits(), 0);
246        assert_eq!(y.coefficient(), x.coefficient());
247        let x = Decimal::new_raw(123456789, 5);
248        let y = x.floor();
249        assert_eq!(y.coefficient(), 1234);
250        assert_eq!(y.n_frac_digits(), 0);
251        let z = y.floor();
252        assert_eq!(y.coefficient(), z.coefficient());
253        let x = Decimal::new_raw(-987, 9);
254        let y = x.floor();
255        assert_eq!(y.coefficient(), -1);
256        assert_eq!(y.n_frac_digits(), 0);
257        let z = y.floor();
258        assert_eq!(y.coefficient(), z.coefficient());
259        let a = &x;
260        let b = a.floor();
261        assert_eq!(b.coefficient(), y.coefficient());
262    }
263
264    #[test]
265    fn test_ceil() {
266        let x = Decimal::new_raw(123, 0);
267        let y = x.ceil();
268        assert_eq!(y.coefficient(), x.coefficient());
269        assert_eq!(y.n_frac_digits(), 0);
270        let x = Decimal::new_raw(123400001, 5);
271        let y = x.ceil();
272        assert_eq!(y.coefficient(), 1235);
273        assert_eq!(y.n_frac_digits(), 0);
274        let z = y.ceil();
275        assert_eq!(y.coefficient(), z.coefficient());
276        let x = Decimal::new_raw(-987, 6);
277        let y = x.ceil();
278        assert_eq!(y.coefficient(), 0);
279        assert_eq!(y.n_frac_digits(), 0);
280        let z = y.ceil();
281        assert_eq!(y.coefficient(), z.coefficient());
282        let a = &x;
283        let b = a.ceil();
284        assert_eq!(b.coefficient(), y.coefficient());
285    }
286
287    #[test]
288    fn test_trunc() {
289        let x = Decimal::new_raw(12345, 0);
290        let y = x.trunc();
291        assert_eq!(x.coefficient(), y.coefficient());
292        assert_eq!(y.n_frac_digits(), 0);
293        let x = Decimal::new_raw(98765, 3);
294        let y = x.trunc();
295        assert_eq!(y.coefficient(), 98);
296        assert_eq!(y.n_frac_digits(), 0);
297        let x = Decimal::new_raw(999999, 7);
298        let y = x.trunc();
299        assert_eq!(y.coefficient(), 0);
300        assert_eq!(y.n_frac_digits(), 0);
301        let a = &x;
302        let b = a.trunc();
303        assert_eq!(b.coefficient(), y.coefficient());
304    }
305
306    #[test]
307    fn test_fract() {
308        let x = Decimal::new_raw(12345, 0);
309        let y = x.fract();
310        assert_eq!(y.coefficient(), 0);
311        assert_eq!(y.n_frac_digits(), 0);
312        let x = Decimal::new_raw(987654, 3);
313        let y = x.fract();
314        assert_eq!(y.coefficient(), 654);
315        assert_eq!(y.n_frac_digits(), 3);
316        let x = Decimal::new_raw(9999, 5);
317        let y = x.fract();
318        assert_eq!(y.coefficient(), 9999);
319        assert_eq!(y.n_frac_digits(), 5);
320        let a = &x;
321        let b = a.fract();
322        assert_eq!(b.coefficient(), y.coefficient());
323    }
324}