polars_core/chunked_array/arithmetic/
decimal.rs

1use polars_compute::decimal::{
2    DEC128_MAX_PREC, dec128_add, dec128_div, dec128_mul, dec128_rescale, dec128_sub,
3};
4
5use super::*;
6use crate::prelude::arity::broadcast_try_binary_elementwise;
7
8impl Add for &DecimalChunked {
9    type Output = PolarsResult<DecimalChunked>;
10
11    fn add(self, rhs: Self) -> Self::Output {
12        let left_s = self.scale();
13        let right_s = rhs.scale();
14        let scale = left_s.max(right_s);
15        let prec = DEC128_MAX_PREC;
16        let phys = broadcast_try_binary_elementwise(
17            self.physical(),
18            rhs.physical(),
19            |opt_l, opt_r| {
20                let (Some(l), Some(r)) = (opt_l, opt_r) else {
21                    return PolarsResult::Ok(None);
22                };
23                let ls = dec128_rescale(l, left_s, prec, scale).ok_or_else(|| {
24                    polars_err!(ComputeError: "overflow in Decimal cast for {l} from scale {left_s} to {scale}")
25                })?;
26                let rs = dec128_rescale(r, right_s, prec, scale).ok_or_else(|| {
27                    polars_err!(ComputeError: "overflow in Decimal cast for {r} from scale {right_s} to {scale}")
28                })?;
29                let ret = dec128_add(ls, rs, prec).ok_or_else(
30                    || polars_err!(ComputeError: "overflow in decimal addition for {ls} + {rs}"),
31                )?;
32                Ok(Some(ret))
33            },
34        );
35        Ok(phys?.into_decimal_unchecked(prec, scale))
36    }
37}
38
39impl Sub for &DecimalChunked {
40    type Output = PolarsResult<DecimalChunked>;
41
42    fn sub(self, rhs: Self) -> Self::Output {
43        let left_s = self.scale();
44        let right_s = rhs.scale();
45        let scale = left_s.max(right_s);
46        let prec = DEC128_MAX_PREC;
47        let phys = broadcast_try_binary_elementwise(
48            self.physical(),
49            rhs.physical(),
50            |opt_l, opt_r| {
51                let (Some(l), Some(r)) = (opt_l, opt_r) else {
52                    return PolarsResult::Ok(None);
53                };
54                let ls = dec128_rescale(l, left_s, prec, scale).ok_or_else(|| {
55                    polars_err!(ComputeError: "overflow in Decimal cast for {l} from scale {left_s} to {scale}")
56                })?;
57                let rs = dec128_rescale(r, right_s, prec, scale).ok_or_else(|| {
58                    polars_err!(ComputeError: "overflow in Decimal cast for {r} from scale {right_s} to {scale}")
59                })?;
60                let ret = dec128_sub(ls, rs, prec).ok_or_else(
61                    || polars_err!(ComputeError: "overflow in decimal subtraction for {ls} - {rs}"),
62                )?;
63                Ok(Some(ret))
64            },
65        );
66        Ok(phys?.into_decimal_unchecked(prec, scale))
67    }
68}
69
70impl Mul for &DecimalChunked {
71    type Output = PolarsResult<DecimalChunked>;
72
73    fn mul(self, rhs: Self) -> Self::Output {
74        let left_s = self.scale();
75        let right_s = rhs.scale();
76        let scale = left_s.max(right_s);
77        let prec = DEC128_MAX_PREC;
78        let phys = broadcast_try_binary_elementwise(
79            self.physical(),
80            rhs.physical(),
81            |opt_l, opt_r| {
82                let (Some(l), Some(r)) = (opt_l, opt_r) else {
83                    return PolarsResult::Ok(None);
84                };
85                let ls = dec128_rescale(l, left_s, prec, scale).ok_or_else(|| {
86                    polars_err!(ComputeError: "overflow in Decimal cast for {l} from scale {left_s} to {scale}")
87                })?;
88                let rs = dec128_rescale(r, right_s, prec, scale).ok_or_else(|| {
89                    polars_err!(ComputeError: "overflow in Decimal cast for {r} from scale {right_s} to {scale}")
90                })?;
91                let ret = dec128_mul(ls, rs, prec, scale).ok_or_else(|| {
92                    polars_err!(ComputeError: "overflow in decimal multiplication for {ls} * {rs}")
93                })?;
94                Ok(Some(ret))
95            },
96        );
97        Ok(phys?.into_decimal_unchecked(prec, scale))
98    }
99}
100
101impl Div for &DecimalChunked {
102    type Output = PolarsResult<DecimalChunked>;
103
104    fn div(self, rhs: Self) -> Self::Output {
105        let left_s = self.scale();
106        let right_s = rhs.scale();
107        let scale = left_s.max(right_s);
108        let prec = DEC128_MAX_PREC;
109        let phys = broadcast_try_binary_elementwise(
110            self.physical(),
111            rhs.physical(),
112            |opt_l, opt_r| {
113                let (Some(l), Some(r)) = (opt_l, opt_r) else {
114                    return PolarsResult::Ok(None);
115                };
116                if r == 0 {
117                    polars_bail!(ComputeError: "division by zero Decimal");
118                }
119                let ls = dec128_rescale(l, left_s, prec, scale).ok_or_else(|| {
120                    polars_err!(ComputeError: "overflow in Decimal cast for {l} from scale {left_s} to {scale}")
121                })?;
122                let rs = dec128_rescale(r, right_s, prec, scale).ok_or_else(|| {
123                    polars_err!(ComputeError: "overflow in Decimal cast for {r} from scale {right_s} to {scale}")
124                })?;
125                let ret = dec128_div(ls, rs, prec, scale).ok_or_else(
126                    || polars_err!(ComputeError: "overflow in decimal division for {ls} / {rs}"),
127                )?;
128                Ok(Some(ret))
129            },
130        );
131        Ok(phys?.into_decimal_unchecked(prec, scale))
132    }
133}