rust_fixed_point_decimal/binops/
mul_rounded.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/binops/mul_rounded.rs $
8// $Revision: 2021-11-01T15:06:20+01:00 $
9
10use std::cmp::Ordering;
11
12use rust_fixed_point_decimal_core::ten_pow;
13
14use crate::{
15    prec_constraints::{PrecLimitCheck, True},
16    rounding::div_i128_rounded,
17    Decimal, MAX_PREC,
18};
19
20/// Multiplication giving a result rounded to fit a `Result` type.
21pub trait MulRounded<Rhs, Result = Self> {
22    /// Returns `self` * `other`, rounded as `Result`.
23    fn mul_rounded(self, rhs: Rhs) -> Result;
24}
25
26impl<const P: u8, const Q: u8, const R: u8> MulRounded<Decimal<Q>, Decimal<R>>
27    for Decimal<P>
28where
29    PrecLimitCheck<{ P <= MAX_PREC }>: True,
30    PrecLimitCheck<{ Q <= MAX_PREC }>: True,
31    PrecLimitCheck<{ R <= MAX_PREC }>: True,
32{
33    #[inline(always)]
34    fn mul_rounded(self, other: Decimal<Q>) -> Decimal<R> {
35        match R.cmp(&(P + Q)) {
36            Ordering::Equal => Decimal::<R> {
37                coeff: self.coeff * other.coeff,
38            },
39            Ordering::Less => Decimal::<R> {
40                coeff: div_i128_rounded(
41                    self.coeff * other.coeff,
42                    ten_pow(P + Q - R),
43                    None,
44                ),
45            },
46            Ordering::Greater => Decimal::<R> {
47                coeff: self.coeff * other.coeff * ten_pow(R - P - Q),
48            },
49        }
50    }
51}
52
53forward_ref_binop_rounded!(impl MulRounded, mul_rounded);
54
55#[cfg(test)]
56mod mul_rounded_decimal_tests {
57    use super::*;
58
59    #[test]
60    fn test_mul_rounded_less_prec() {
61        let x = Decimal::<2>::new_raw(12345);
62        let z: Decimal<2> = x.mul_rounded(x);
63        assert_eq!(z.coeff, 1523990);
64        let y = Decimal::<4>::new_raw(5781);
65        let z: Decimal<1> = x.mul_rounded(y);
66        assert_eq!(z.coeff, 714);
67        let z: Decimal<1> = y.mul_rounded(x);
68        assert_eq!(z.coeff, 714);
69    }
70
71    #[test]
72    fn test_mul_rounded_no_adj_needed() {
73        let x = Decimal::<2>::new_raw(12345);
74        let z: Decimal<4> = x.mul_rounded(x);
75        assert_eq!(z.coeff, 152399025);
76        let y = Decimal::<4>::new_raw(5781);
77        let z: Decimal<6> = x.mul_rounded(y);
78        assert_eq!(z.coeff, 71366445);
79        let z: Decimal<6> = y.mul_rounded(x);
80        assert_eq!(z.coeff, 71366445);
81    }
82
83    #[test]
84    fn test_mul_rounded_greater_prec() {
85        let x = Decimal::<2>::new_raw(12345);
86        let z: Decimal<6> = x.mul_rounded(x);
87        assert_eq!(z.coeff, 15239902500);
88        let y = Decimal::<4>::new_raw(5781);
89        let z: Decimal<7> = x.mul_rounded(y);
90        assert_eq!(z.coeff, 713664450);
91        let z: Decimal<9> = y.mul_rounded(x);
92        assert_eq!(z.coeff, 71366445000);
93    }
94
95    #[test]
96    fn test_mul_rounded_ref() {
97        let x = Decimal::<3>::new_raw(12345);
98        let y = Decimal::<1>::new_raw(12345);
99        let z: Decimal<2> = x.mul_rounded(y);
100        let a: Decimal<2> = MulRounded::mul_rounded(&x, y);
101        assert_eq!(a.coeff, z.coeff);
102        let a: Decimal<2> = MulRounded::mul_rounded(x, &y);
103        assert_eq!(a.coeff, z.coeff);
104        let a: Decimal<2> = MulRounded::mul_rounded(&x, &y);
105        assert_eq!(a.coeff, z.coeff);
106    }
107}