fpdec/binops/
mul_rounded.rs1use fpdec_core::{
11 i128_div_rounded, i128_mul_div_ten_pow_rounded, ten_pow,
12 MAX_N_FRAC_DIGITS,
13};
14
15use crate::{Decimal, DecimalError};
16
17pub trait MulRounded<Rhs = Self> {
20 type Output;
22
23 fn mul_rounded(self, rhs: Rhs, n_frac_digits: u8) -> Self::Output;
25}
26
27pub(crate) fn checked_mul_rounded(
28 x: Decimal,
29 y: Decimal,
30 n_frac_digits: u8,
31) -> Option<Decimal> {
32 let max_n_frac_digits = x.n_frac_digits + y.n_frac_digits;
33 if n_frac_digits >= max_n_frac_digits {
34 Some(Decimal {
36 coeff: x.coeff.checked_mul(y.coeff)?,
37 n_frac_digits: max_n_frac_digits,
38 })
39 } else {
40 let shift = max_n_frac_digits - n_frac_digits;
41 if let Some(coeff) = x.coeff.checked_mul(y.coeff) {
42 Some(Decimal {
43 coeff: i128_div_rounded(coeff, ten_pow(shift), None),
44 n_frac_digits,
45 })
46 } else {
47 let coeff =
48 i128_mul_div_ten_pow_rounded(x.coeff, y.coeff, shift, None)?;
49 Some(Decimal {
50 coeff,
51 n_frac_digits,
52 })
53 }
54 }
55}
56
57impl MulRounded<Self> for Decimal {
58 type Output = Self;
59
60 #[inline]
61 fn mul_rounded(self, rhs: Self, n_frac_digits: u8) -> Self::Output {
62 #[allow(clippy::manual_assert)]
63 if n_frac_digits > MAX_N_FRAC_DIGITS {
64 panic!("{}", DecimalError::MaxNFracDigitsExceeded);
65 }
66 if self.eq_zero() || rhs.eq_zero() {
67 return Self::ZERO;
68 }
69 if let Some(res) = checked_mul_rounded(self, rhs, n_frac_digits) {
70 res
71 } else {
72 panic!("{}", DecimalError::InternalOverflow);
73 }
74 }
75}
76
77forward_ref_binop_rounded!(impl MulRounded, mul_rounded);
78
79#[cfg(test)]
80mod mul_rounded_decimal_tests {
81 use super::*;
82
83 #[test]
84 fn test_mul_rounded_less_n_frac_digits() {
85 let x = Decimal::new_raw(12345, 2);
86 let z = x.mul_rounded(x, 2);
87 assert_eq!(z.coefficient(), 1523990);
88 assert_eq!(z.n_frac_digits(), 2);
89 let y = Decimal::new_raw(5781, 4);
90 let z = x.mul_rounded(y, 1);
91 assert_eq!(z.coefficient(), 714);
92 assert_eq!(z.n_frac_digits(), 1);
93 let z = y.mul_rounded(x, 1);
94 assert_eq!(z.coefficient(), 714);
95 assert_eq!(z.n_frac_digits(), 1);
96 }
97
98 #[test]
99 fn test_mul_rounded_no_adj_needed() {
100 let x = Decimal::new_raw(12345, 2);
101 let z = x.mul_rounded(x, 4);
102 assert_eq!(z.coefficient(), 152399025);
103 assert_eq!(z.n_frac_digits(), 4);
104 let y = Decimal::new_raw(5781, 4);
105 let z = x.mul_rounded(y, 10);
106 assert_eq!(z.coefficient(), 71366445);
107 assert_eq!(z.n_frac_digits(), 6);
108 let z = y.mul_rounded(x, 7);
109 assert_eq!(z.coefficient(), 71366445);
110 assert_eq!(z.n_frac_digits(), 6);
111 }
112
113 #[test]
114 fn test_mul_rounded_ref() {
115 let x = Decimal::new_raw(12345, 3);
116 let y = Decimal::new_raw(12345, 1);
117 let z = x.mul_rounded(y, 2);
118 let a = MulRounded::mul_rounded(&x, y, 2);
119 assert_eq!(a.coefficient(), z.coefficient());
120 let a = MulRounded::mul_rounded(x, &y, 2);
121 assert_eq!(a.coefficient(), z.coefficient());
122 let a = MulRounded::mul_rounded(&x, &y, 2);
123 assert_eq!(a.coefficient(), z.coefficient());
124 }
125}