bigdecimal/
impl_ops_rem.rs

1//! Remainder implementations
2
3use super::*;
4
5
6impl Rem<BigDecimal> for BigDecimal {
7    type Output = BigDecimal;
8
9    #[inline]
10    fn rem(self, other: BigDecimal) -> BigDecimal {
11        let scale = cmp::max(self.scale, other.scale);
12
13        let num = self.take_and_scale(scale).int_val;
14        let den = other.take_and_scale(scale).int_val;
15
16        BigDecimal::new(num % den, scale)
17    }
18}
19
20impl Rem<&BigDecimal> for BigDecimal {
21    type Output = BigDecimal;
22
23    #[inline]
24    fn rem(self, other: &BigDecimal) -> BigDecimal {
25        let scale = cmp::max(self.scale, other.scale);
26        let num = self.take_and_scale(scale).int_val;
27        let den = &other.int_val;
28
29        let result = if scale == other.scale {
30            num % den
31        } else {
32            num % (den * ten_to_the((scale - other.scale) as u64))
33        };
34        BigDecimal::new(result, scale)
35    }
36}
37
38impl Rem<BigDecimal> for &BigDecimal {
39    type Output = BigDecimal;
40
41    #[inline]
42    fn rem(self, other: BigDecimal) -> BigDecimal {
43        let scale = cmp::max(self.scale, other.scale);
44        let num = &self.int_val;
45        let den = other.take_and_scale(scale).int_val;
46
47        let result = if scale == self.scale {
48            num % den
49        } else {
50            let scaled_num = num * ten_to_the((scale - self.scale) as u64);
51            scaled_num % den
52        };
53
54        BigDecimal::new(result, scale)
55    }
56}
57
58impl Rem<&BigDecimal> for &BigDecimal {
59    type Output = BigDecimal;
60
61    #[inline]
62    fn rem(self, other: &BigDecimal) -> BigDecimal {
63        let scale = cmp::max(self.scale, other.scale);
64        let num = &self.int_val;
65        let den = &other.int_val;
66
67        let result = match self.scale.cmp(&other.scale) {
68            Ordering::Equal => num % den,
69            Ordering::Less => {
70                let scaled_num = num * ten_to_the((scale - self.scale) as u64);
71                scaled_num % den
72            }
73            Ordering::Greater => {
74                let scaled_den = den * ten_to_the((scale - other.scale) as u64);
75                num % scaled_den
76            }
77        };
78        BigDecimal::new(result, scale)
79    }
80}
81
82impl RemAssign<&BigDecimal> for BigDecimal {
83    fn rem_assign(&mut self, other: &BigDecimal) {
84        let rem = (&*self).rem(other);
85        *self = rem;
86    }
87}
88
89
90#[cfg(test)]
91mod test {
92    use super::*;
93    use paste::paste;
94
95    macro_rules! impl_case {
96        ($a:literal % $b:literal => $c:literal ) => {
97            paste! {
98                impl_case!([< case_ $a _ $b >]: $a % $b => $c);
99            }
100        };
101        ($name:ident: $a:literal % $b:literal => $c:literal ) => {
102            #[test]
103            fn $name() {
104                let mut a: BigDecimal = $a.parse().unwrap();
105                let b: BigDecimal = $b.parse().unwrap();
106                let c: BigDecimal = $c.parse().unwrap();
107
108                assert_eq!(a.clone() % b.clone(), c);
109
110                assert_eq!(a.clone() % &b, c);
111                assert_eq!(&a % b.clone(), c);
112                assert_eq!(&a % &b, c);
113
114                a %= &b;
115                assert_eq!(a, c);
116            }
117        };
118    }
119
120    impl_case!("100" % "5" => "0");
121    impl_case!("2e1" % "1" => "0");
122    impl_case!("2" % "1" => "0");
123    impl_case!("1" % "3" => "1");
124    impl_case!("1" % "5e-1" => "0");
125    impl_case!("15e-1" % "1" => "0.5");
126    impl_case!("1" % "3e-2" => "1e-2");
127    impl_case!("10" % "3e-3" => "0.001");
128    impl_case!("3" % "2" => "1");
129    impl_case!("1234e-2" % "1233e-3" => "0.01");
130
131    impl_case!(case_neg3_2: "-3" % "2" => "-1");
132    impl_case!(case_3_neg2: "3" % "-2" => "1");
133    impl_case!(case_neg3_neg2: "3" % "-2" => "1");
134
135    impl_case!(case_neg95eneg1_515eneg2: "-9.5" % "5.15" => "-4.35");
136
137
138    #[cfg(property_tests)]
139    mod prop {
140        use super::*;
141        use proptest::*;
142        use num_traits::FromPrimitive;
143
144        proptest! {
145            #[test]
146            fn quotient_and_remainder(f: f32, g: f32) {
147                // ignore non-normal numbers
148                prop_assume!(f.is_normal());
149                prop_assume!(g.is_normal());
150                prop_assume!(!g.is_zero());
151
152                let (f, g) = if f.abs() > g.abs() {
153                    (f, g)
154                } else {
155                    (g, f)
156                };
157
158                let a = BigDecimal::from_f32(f).unwrap();
159                let b = BigDecimal::from_f32(g).unwrap();
160
161                let r = &a % &b;
162                let q = (&a / &b).with_scale(0);
163                assert_eq!(a, q * b + r);
164            }
165        }
166    }
167}