bigdecimal/
context.rs

1//! Define arithmetical context
2//!
3
4use crate::*;
5use stdlib::num::NonZeroU64;
6
7use arithmetic::store_carry;
8use rounding::NonDigitRoundingData;
9
10
11// const DEFAULT_PRECISION: u64 = ${RUST_BIGDECIMAL_DEFAULT_PRECISION} or 100;
12include!(concat!(env!("OUT_DIR"), "/default_precision.rs"));
13
14
15/// Mathematical Context
16///
17/// Stores rules for numerical operations, such as how to round and
18/// number of digits to keep.
19///
20/// Defaults are defined at compile time, determined by environment
21/// variables:
22///
23/// | Variable                                |   Description   | default  |
24/// |-----------------------------------------|-----------------|----------|
25/// | `RUST_BIGDECIMAL_DEFAULT_PRECISION`     | digit precision | 100      |
26/// | `RUST_BIGDECIMAL_DEFAULT_ROUNDING_MODE` | rounding-mode   | HalfEven |
27///
28/// It is recommended that the user set explicit values of a Context and *not*
29/// rely on compile time constants, but the option is there if necessary.
30///
31#[derive(#[automatically_derived]
impl ::core::fmt::Debug for Context {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field2_finish(f, "Context",
            "precision", &self.precision, "rounding", &&self.rounding)
    }
}Debug, #[automatically_derived]
impl ::core::clone::Clone for Context {
    #[inline]
    fn clone(&self) -> Context {
        Context {
            precision: ::core::clone::Clone::clone(&self.precision),
            rounding: ::core::clone::Clone::clone(&self.rounding),
        }
    }
}Clone)]
32pub struct Context {
33    /// total number of digits
34    precision: NonZeroU64,
35    /// how to round
36    rounding: RoundingMode,
37}
38
39impl Context {
40    /// Create context with precision and rounding mode
41    pub fn new(precision: NonZeroU64, rounding: RoundingMode) -> Self {
42        Context {
43            precision: precision,
44            rounding: rounding,
45        }
46    }
47
48    /// Copy context with new precision value
49    pub fn with_precision(&self, precision: NonZeroU64) -> Self {
50        Self {
51            precision: precision,
52            ..*self
53        }
54    }
55
56    /// Copy context with new precision value
57    pub fn with_prec<T: ToPrimitive>(&self, precision: T) -> Option<Self> {
58        precision
59            .to_u64()
60            .and_then(NonZeroU64::new)
61            .map(|prec| self.with_precision(prec))
62    }
63
64    /// Copy context with new rounding mode
65    pub fn with_rounding_mode(&self, mode: RoundingMode) -> Self {
66        Self {
67            rounding: mode,
68            ..*self
69        }
70    }
71
72    /// Rounding mode 'Down' for truncating results when
73    /// proper rounding is not necessary
74    pub(crate) fn new_truncating(prec: u64) -> Self {
75        Self {
76            rounding: RoundingMode::Down,
77            precision: NonZeroU64::new(prec.max(1)).unwrap(),
78        }
79    }
80
81    /// Return maximum precision
82    pub fn precision(&self) -> NonZeroU64 {
83        self.precision
84    }
85
86    /// Return rounding mode
87    pub fn rounding_mode(&self) -> RoundingMode {
88        self.rounding
89    }
90
91    /// Round decimal to precision in this context, using rounding-mode
92    pub fn round_decimal(&self, n: BigDecimal) -> BigDecimal {
93        n.with_precision_round(self.precision(), self.rounding_mode())
94    }
95
96    /// Round decimal to precision in this context, using rounding-mode
97    pub fn round_decimal_ref<'a, D: Into<BigDecimalRef<'a>>>(&self, n: D) -> BigDecimal {
98        let d = n.into().to_owned();
99        d.with_precision_round(self.precision(), self.rounding_mode())
100    }
101
102    /// Round the bigint to the context's precision, returning it along with
103    /// the scale indicating how man digits were removed
104    #[allow(dead_code)]
105    pub(crate) fn round_bigint(
106        self, n: num_bigint::BigInt
107    ) -> WithScale<num_bigint::BigInt> {
108        self.rounding.round_bigint_to_prec(n, self.precision)
109    }
110
111    /// Round the biguint to the context's precision, returning it along with
112    /// the scale indicating how man digits were removed
113    #[allow(dead_code)]
114    pub(crate) fn round_biguint(
115        self, n: num_bigint::BigUint
116    ) -> WithScale<num_bigint::BigUint> {
117        let ndrd = NonDigitRoundingData { mode: self.rounding, sign: Sign::Plus };
118        ndrd.round_biguint_to_prec(n, self.precision)
119    }
120
121    /// Round digits x and y with the rounding mode
122    #[allow(dead_code)]
123    pub(crate) fn round_pair(&self, sign: Sign, x: u8, y: u8, trailing_zeros: bool) -> u8 {
124        self.rounding.round_pair(sign, (x, y), trailing_zeros)
125    }
126
127    /// Round digits x and y with the rounding mode
128    #[allow(dead_code)]
129    pub(crate) fn round_pair_with_carry(
130        &self,
131        sign: Sign,
132        x: u8,
133        y: u8,
134        trailing_zeros: bool,
135        carry: &mut u8,
136    ) -> u8 {
137        self.rounding.round_pair_with_carry(sign, (x, y), trailing_zeros, carry)
138    }
139
140    /// Multiply two decimals, returning product rounded to this Context's precision
141    ///
142    /// ```
143    /// # use bigdecimal::{BigDecimal, Context};
144    /// let x: BigDecimal = "1.5".parse().unwrap();
145    /// let y: BigDecimal = "3.1415926".parse().unwrap();
146    ///
147    /// let ctx = Context::default().with_prec(5).unwrap();
148    /// let product = ctx.multiply(&x, &y);
149    ///
150    /// // rounds to 5 digits of precision
151    /// assert_eq!(product, "4.7124".parse().unwrap());
152    /// // does not equal the 'full' precision
153    /// assert_ne!(product, "4.71238890".parse().unwrap());
154    /// ```
155    ///
156    pub fn multiply<'a, L, R>(&self, lhs: L, rhs: R) -> BigDecimal
157    where
158        L: Into<BigDecimalRef<'a>>,
159        R: Into<BigDecimalRef<'a>>,
160    {
161        use arithmetic::multiplication::multiply_decimals_with_context;
162
163        let mut result = BigDecimal::zero();
164        multiply_decimals_with_context(&mut result, lhs, rhs, self);
165        result
166    }
167
168    /// Calculate `1/n`, rounding at this Context's precision
169    ///
170    /// If n is zero, return zero.
171    ///
172    /// ```
173    /// # use bigdecimal::{BigDecimal, Context};
174    /// let x: BigDecimal = "3".parse().unwrap();
175    ///
176    /// let ctx = Context::default().with_prec(5).unwrap();
177    /// let one_over_three = ctx.invert(&x);
178    ///
179    /// // rounds to 5 digits of precision
180    /// assert_eq!(one_over_three, "0.33333".parse().unwrap());
181    /// ```
182    ///
183    pub fn invert<'a, T: Into<BigDecimalRef<'a>>>(&self, n: T) -> BigDecimal {
184        n.into().inverse_with_context(self)
185    }
186}
187
188impl stdlib::default::Default for Context {
189    fn default() -> Self {
190        Self {
191            precision: NonZeroU64::new(DEFAULT_PRECISION).unwrap(),
192            rounding: RoundingMode::default(),
193        }
194    }
195}
196
197impl Context {
198    /// Add two big digit references
199    pub fn add_refs<'a, 'b, A, B>(&self, a: A, b: B) -> BigDecimal
200    where
201        A: Into<BigDecimalRef<'a>>,
202        B: Into<BigDecimalRef<'b>>,
203    {
204        let mut sum = BigDecimal::zero();
205        self.add_refs_into(a, b, &mut sum);
206        sum
207    }
208
209    /// Add two decimal refs, storing value in dest
210    pub fn add_refs_into<'a, 'b, A, B>(&self, a: A, b: B, dest: &mut BigDecimal)
211    where
212        A: Into<BigDecimalRef<'a>>,
213        B: Into<BigDecimalRef<'b>>,
214    {
215        let sum = a.into() + b.into();
216        *dest = sum.with_precision_round(self.precision, self.rounding)
217    }
218}
219
220
221#[cfg(test)]
222mod test_context {
223    use super::*;
224
225    #[test]
226    fn constructor_and_setters() {
227        let ctx = Context::default();
228        let c = ctx.with_prec(44).unwrap();
229        assert_eq!(c.precision.get(), 44);
230        assert_eq!(c.rounding, RoundingMode::HalfEven);
231
232        let c = c.with_rounding_mode(RoundingMode::Down);
233        assert_eq!(c.precision.get(), 44);
234        assert_eq!(c.rounding, RoundingMode::Down);
235    }
236
237    #[test]
238    fn sum_two_references() {
239        use stdlib::ops::Neg;
240
241        let ctx = Context::default();
242        let a: BigDecimal = "209682.134972197168613072130300".parse().unwrap();
243        let b: BigDecimal = "3.0782968222271332463325639E-12".parse().unwrap();
244
245        let sum = ctx.add_refs(&a, &b);
246        assert_eq!(sum, "209682.1349721971716913689525271332463325639".parse().unwrap());
247
248        // make negative copy of b without cloning values
249        let neg_b = b.to_ref().neg();
250
251        let sum = ctx.add_refs(&a, neg_b);
252        assert_eq!(sum, "209682.1349721971655347753080728667536674361".parse().unwrap());
253
254        let sum = ctx.with_prec(27).unwrap().with_rounding_mode(RoundingMode::Up).add_refs(&a, neg_b);
255        assert_eq!(sum, "209682.134972197165534775309".parse().unwrap());
256    }
257
258    mod round_decimal_ref {
259        use super::*;
260
261        #[test]
262        fn case_bigint_1234567_prec3() {
263            let ctx = Context::default().with_prec(3).unwrap();
264            let i = BigInt::from(1234567);
265            let d = ctx.round_decimal_ref(&i);
266            assert_eq!(d.int_val, 123.into());
267            assert_eq!(d.scale, -4);
268        }
269
270        #[test]
271        fn case_bigint_1234500_prec4_halfup() {
272            let ctx = Context::default()
273                              .with_prec(4).unwrap()
274                              .with_rounding_mode(RoundingMode::HalfUp);
275            let i = BigInt::from(1234500);
276            let d = ctx.round_decimal_ref(&i);
277            assert_eq!(d.int_val, 1235.into());
278            assert_eq!(d.scale, -3);
279        }
280
281        #[test]
282        fn case_bigint_1234500_prec4_halfeven() {
283            let ctx = Context::default()
284                              .with_prec(4).unwrap()
285                              .with_rounding_mode(RoundingMode::HalfEven);
286            let i = BigInt::from(1234500);
287            let d = ctx.round_decimal_ref(&i);
288            assert_eq!(d.int_val, 1234.into());
289            assert_eq!(d.scale, -3);
290        }
291
292        #[test]
293        fn case_bigint_1234567_prec10() {
294            let ctx = Context::default().with_prec(10).unwrap();
295            let i = BigInt::from(1234567);
296            let d = ctx.round_decimal_ref(&i);
297            assert_eq!(d.int_val, 1234567000.into());
298            assert_eq!(d.scale, 3);
299        }
300    }
301}