rust_fixed_point_decimal/
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: 2021-10-29T12:14:58+02:00 $
9
10use std::ops::Neg;
11
12use num::Integer;
13use rust_fixed_point_decimal_core::ten_pow;
14
15use crate::{
16    prec_constraints::{PrecLimitCheck, True},
17    Decimal, MAX_PREC,
18};
19
20impl<const P: u8> Neg for Decimal<P>
21where
22    PrecLimitCheck<{ P <= MAX_PREC }>: True,
23{
24    type Output = Self;
25
26    /// Returns -self.
27    ///
28    /// # Panics
29    ///
30    /// Panics with 'attempt to negate with overflow' when called on
31    /// `Decimal::<P>::MIN`!
32    fn neg(self) -> Self::Output {
33        Self::Output { coeff: -self.coeff }
34    }
35}
36
37impl<const P: u8> Neg for &Decimal<P>
38where
39    PrecLimitCheck<{ P <= MAX_PREC }>: True,
40{
41    type Output = <Decimal<P> as Neg>::Output;
42
43    /// Returns -self.
44    ///
45    /// #Panics
46    ///
47    /// Panics with 'attempt to negate with overflow' when called on
48    /// `Decimal::<P>::MIN`!
49    fn neg(self) -> Self::Output {
50        Self::Output { coeff: -self.coeff }
51    }
52}
53
54impl<const P: u8> Decimal<P>
55where
56    PrecLimitCheck<{ P <= MAX_PREC }>: True,
57{
58    /// Returns the absolute value of `self`.
59    #[inline]
60    pub fn abs(&self) -> Self {
61        Self {
62            coeff: self.coeff.abs(),
63        }
64    }
65
66    /// Returns the largest integral value <= `self`.
67    ///
68    /// # Panics
69    ///
70    /// Panics with 'attempt to multiply with overflow' when called on a value
71    /// less than
72    ///
73    /// `(Decimal::<P>::MIN / 10 ^ P) * 10 ^ P` !
74    ///
75    /// # Examples
76    ///
77    /// ```rust
78    /// # use rust_fixed_point_decimal::Dec;
79    /// let d = Dec!(17.5);
80    /// assert_eq!(d.floor().to_string(), "17.0");
81    /// let d = Dec!(-17.050);
82    /// assert_eq!(d.floor().to_string(), "-18.000");
83    /// ```
84    #[inline]
85    pub fn floor(&self) -> Self {
86        match P {
87            0 => self.clone(),
88            _ => Self {
89                coeff: self.coeff.div_floor(&ten_pow(P)) * ten_pow(P),
90            },
91        }
92    }
93
94    /// Returns the smallest integral value >= `self`.
95    ///
96    /// # Panics
97    ///
98    /// Panics with 'attempt to multiply with overflow' when called on a value
99    /// greater than
100    ///
101    /// `(Decimal::<P>::MAX / 10 ^ P) * 10 ^ P` !
102    ///
103    /// # Examples
104    ///
105    /// ```rust
106    /// # use rust_fixed_point_decimal::Dec;
107    /// let d = Dec!(17.5);
108    /// assert_eq!(d.ceil().to_string(), "18.0");
109    /// let d = Dec!(-17.50);
110    /// assert_eq!(d.ceil().to_string(), "-17.00");
111    /// ```
112    #[inline]
113    pub fn ceil(&self) -> Self {
114        match P {
115            0 => self.clone(),
116            _ => Self {
117                coeff: self.coeff.div_ceil(&ten_pow(P)) * ten_pow(P),
118            },
119        }
120    }
121
122    /// Returns the integral part of `self`.
123    ///
124    /// # Examples
125    ///
126    /// ```rust
127    /// # use rust_fixed_point_decimal::Dec;
128    /// let d = Dec!(17.5);
129    /// assert_eq!(d.trunc().to_string(), "17.0");
130    /// let d = Dec!(-17.55555);
131    /// assert_eq!(d.trunc().to_string(), "-17.00000");
132    /// ```
133    #[inline]
134    pub fn trunc(&self) -> Self {
135        if P == 0 {
136            *self
137        } else {
138            Self {
139                coeff: (self.coeff / ten_pow(P)) * ten_pow(P),
140            }
141        }
142    }
143
144    /// Returns the fractional part of `self`.
145    ///
146    /// # Examples
147    ///
148    /// ```rust
149    /// # use rust_fixed_point_decimal::Dec;
150    /// let d = Dec!(17.050);
151    /// assert_eq!(d.fract().to_string(), "0.050");
152    /// let d = Dec!(-17.5);
153    /// assert_eq!(d.fract().to_string(), "-0.5");
154    /// ```
155    #[inline]
156    pub fn fract(&self) -> Self {
157        if P == 0 {
158            Self::ZERO
159        } else {
160            Self {
161                coeff: self.coeff % ten_pow(P),
162            }
163        }
164    }
165}
166
167#[cfg(test)]
168mod tests {
169    use crate::Decimal;
170
171    #[test]
172    fn test_neg() {
173        let val = 1234567890i128;
174        let x: Decimal<2> = Decimal::new_raw(val);
175        let y = -x;
176        assert_eq!(x.coeff, -y.coeff);
177        let z = -y;
178        assert_eq!(x.coeff, z.coeff);
179        let a = &x;
180        let b = -a;
181        assert_eq!(a.coeff, -b.coeff);
182    }
183
184    #[test]
185    fn test_neg_corner_cases_ok() {
186        let x = Decimal::<2>::MAX;
187        let y = -x;
188        assert_eq!(x.coeff, -y.coeff);
189        let z = -y;
190        assert_eq!(x.coeff, z.coeff);
191    }
192
193    #[test]
194    #[should_panic]
195    fn test_neg_corner_cases_fail() {
196        let x = Decimal::<2>::MIN;
197        let _y = -x;
198    }
199
200    #[test]
201    fn test_abs() {
202        let x = Decimal::<4>::new_raw(-123456789);
203        let y = x.abs();
204        assert_eq!(-x.coeff, y.coeff);
205        let z = y.abs();
206        assert_eq!(y.coeff, z.coeff);
207        let a = &x;
208        let b = a.abs();
209        assert_eq!(-a.coeff, b.coeff);
210    }
211
212    #[test]
213    fn test_floor() {
214        let x = Decimal::<0>::new_raw(123);
215        let y = x.floor();
216        assert_eq!(y.coeff, x.coeff);
217        let x = Decimal::<5>::new_raw(123456789);
218        let y = x.floor();
219        assert_eq!(y.coeff, 123400000);
220        let z = y.floor();
221        assert_eq!(y.coeff, z.coeff);
222        let x = Decimal::<9>::new_raw(-987);
223        let y = x.floor();
224        assert_eq!(y.coeff, -1000000000);
225        let z = y.floor();
226        assert_eq!(y.coeff, z.coeff);
227        let a = &x;
228        let b = a.floor();
229        assert_eq!(b.coeff, y.coeff);
230    }
231
232    #[test]
233    #[should_panic]
234    fn test_floor_overflow() {
235        let x = Decimal::<3>::new_raw((i128::MIN / 1000) * 1000 - 1);
236        let _y = x.floor();
237    }
238
239    #[test]
240    fn test_ceil() {
241        let x = Decimal::<0>::new_raw(123);
242        let y = x.ceil();
243        assert_eq!(y.coeff, x.coeff);
244        let x = Decimal::<5>::new_raw(123400001);
245        let y = x.ceil();
246        assert_eq!(y.coeff, 123500000);
247        let z = y.ceil();
248        assert_eq!(y.coeff, z.coeff);
249        let x = Decimal::<9>::new_raw(-987);
250        let y = x.ceil();
251        assert_eq!(y.coeff, 0);
252        let z = y.ceil();
253        assert_eq!(y.coeff, z.coeff);
254        let a = &x;
255        let b = a.ceil();
256        assert_eq!(b.coeff, y.coeff);
257    }
258
259    #[test]
260    #[should_panic]
261    fn test_ceil_overflow() {
262        let x = Decimal::<2>::new_raw((i128::MAX / 100) * 100 + 1);
263        let _y = x.ceil();
264    }
265
266    #[test]
267    fn test_trunc() {
268        let x = Decimal::<0>::new_raw(12345);
269        let y = x.trunc();
270        assert_eq!(x.coeff, y.coeff);
271        let x = Decimal::<3>::new_raw(98765);
272        let y = x.trunc();
273        assert_eq!(y.coeff, 98000);
274        let x = Decimal::<7>::new_raw(999999);
275        let y = x.trunc();
276        assert_eq!(y.coeff, 0);
277        let a = &x;
278        let b = a.trunc();
279        assert_eq!(b.coeff, y.coeff);
280    }
281
282    #[test]
283    fn test_fract() {
284        let x = Decimal::<0>::new_raw(12345);
285        let y = x.fract();
286        assert_eq!(y.coeff, 0);
287        let x = Decimal::<3>::new_raw(987654);
288        let y = x.fract();
289        assert_eq!(y.coeff, 654);
290        let x = Decimal::<5>::new_raw(9999);
291        let y = x.fract();
292        assert_eq!(y.coeff, 9999);
293        let a = &x;
294        let b = a.fract();
295        assert_eq!(b.coeff, y.coeff);
296    }
297}