bigdecimal/
impl_ops_rem.rs
1use 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 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}