dec/
context.rs

1// Copyright Materialize, Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License in the LICENSE file at the
6// root of this repository, or online at
7//
8//     http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16use std::fmt;
17use std::marker::PhantomData;
18use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign};
19
20use libc::c_uint;
21
22/// A context for performing decimal operations.
23///
24/// Contexts serve two purposes:
25///
26///   * They configure various properties of decimal arithmetic, like the
27///     rounding algorithm to use.
28///
29///   * They accumulate any informational and exceptional conditions raised by
30///     decimal operations. Multiple operations can be performed on a context
31///     and the status need only be checked once at the end. This can improve
32///     performance when performing many decimal operations.
33///
34/// A given context is only valid for use with one decimal type, specified by
35/// the `D` type parameter.
36///
37/// Not all context types support all operations. For example, only the
38/// context for the arbitrary-precision decimal type `Decimal` supports
39/// configuring precision.
40#[derive(Clone)]
41pub struct Context<D> {
42    pub(crate) inner: decnumber_sys::decContext,
43    pub(crate) _phantom: PhantomData<D>,
44}
45
46impl<D> fmt::Debug for Context<D> {
47    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
48        f.debug_struct("Context")
49            .field("clamp", &self.inner.clamp)
50            .field("digits", &self.inner.digits)
51            .field("emax", &self.inner.emax)
52            .field("emin", &self.inner.emin)
53            .field("rounding", &self.rounding())
54            .field("traps", &self.inner.traps)
55            .finish()
56    }
57}
58
59impl<D> Context<D> {
60    /// Returns the context's rounding algorithm.
61    pub fn rounding(&self) -> Rounding {
62        Rounding::from_c(self.inner.round)
63    }
64
65    /// Set's the context's rounding algorithm.
66    pub fn set_rounding(&mut self, rounding: Rounding) {
67        self.inner.round = rounding.to_c();
68    }
69
70    /// Returns the context's status.
71    pub fn status(&self) -> Status {
72        Status {
73            inner: self.inner.status,
74        }
75    }
76
77    /// Sets the context's status.
78    pub fn set_status(&mut self, status: Status) {
79        self.inner.status = status.inner;
80    }
81
82    /// Clears the context's status.
83    pub fn clear_status(&mut self) {
84        self.inner.status = 0;
85    }
86}
87
88/// Algorithms for rounding decimal numbers.
89///
90/// The rounding modes are precisely defined in [The Arithmetic Model][model]
91/// chapter of the General Decimal Arithmetic specification.
92///
93/// [model]: http://speleotrove.com/decimal/damodel.html
94#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
95pub enum Rounding {
96    /// Round towards positive infinity.
97    Ceiling,
98    /// Round towards zero (truncation).
99    Down,
100    /// Round towards negative infinity.
101    Floor,
102    /// Round to nearest; if equidistant, round down.
103    HalfDown,
104    /// Round to nearest; if equidistant, round so that the final digit is even.
105    HalfEven,
106    /// Round to nearest; if equidistant, round up.
107    HalfUp,
108    /// Round away from zero.
109    Up,
110    /// The same as [`Rounding::Up`], except that rounding up only occurs
111    /// if the digit to be rounded up is 0 or 5.
112    ///
113    /// After overflow the result is the same as for [`Rounding::Down`].
114    ZeroFiveUp,
115}
116
117impl Default for Rounding {
118    fn default() -> Rounding {
119        Rounding::HalfEven
120    }
121}
122
123impl Rounding {
124    fn from_c(c: c_uint) -> Rounding {
125        match c {
126            decnumber_sys::DEC_ROUND_CEILING => Rounding::Ceiling,
127            decnumber_sys::DEC_ROUND_DOWN => Rounding::Down,
128            decnumber_sys::DEC_ROUND_FLOOR => Rounding::Floor,
129            decnumber_sys::DEC_ROUND_HALF_DOWN => Rounding::HalfDown,
130            decnumber_sys::DEC_ROUND_HALF_EVEN => Rounding::HalfEven,
131            decnumber_sys::DEC_ROUND_HALF_UP => Rounding::HalfUp,
132            decnumber_sys::DEC_ROUND_UP => Rounding::Up,
133            decnumber_sys::DEC_ROUND_05UP => Rounding::ZeroFiveUp,
134            _ => unreachable!("invalid C rounding value"),
135        }
136    }
137
138    fn to_c(&self) -> c_uint {
139        match self {
140            Rounding::Ceiling => decnumber_sys::DEC_ROUND_CEILING,
141            Rounding::Down => decnumber_sys::DEC_ROUND_DOWN,
142            Rounding::Floor => decnumber_sys::DEC_ROUND_FLOOR,
143            Rounding::HalfDown => decnumber_sys::DEC_ROUND_HALF_DOWN,
144            Rounding::HalfEven => decnumber_sys::DEC_ROUND_HALF_EVEN,
145            Rounding::HalfUp => decnumber_sys::DEC_ROUND_HALF_UP,
146            Rounding::Up => decnumber_sys::DEC_ROUND_UP,
147            Rounding::ZeroFiveUp => decnumber_sys::DEC_ROUND_05UP,
148        }
149    }
150}
151
152/// Represents exceptional conditions resulting from operations on decimal
153/// numbers.
154///
155/// For details about the various exceptional conditions, consult the
156/// [Exceptional Conditions][conditions] chapter of the General Decimal
157/// Arithmetic specification.
158///
159/// [conditions]: http://speleotrove.com/decimal/daexcep.html
160#[derive(Clone, Copy, Eq, PartialEq, Hash)]
161pub struct Status {
162    inner: u32,
163}
164
165impl fmt::Debug for Status {
166    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
167        f.debug_struct("Status")
168            .field("conversion_syntax", &self.conversion_syntax())
169            .field("division_by_zero", &self.division_by_zero())
170            .field("division_impossible", &self.division_impossible())
171            .field("division_undefined", &self.division_undefined())
172            .field("insufficient_storage", &self.insufficient_storage())
173            .field("inexact", &self.inexact())
174            .field("invalid_context", &self.invalid_context())
175            .field("invalid_operation", &self.invalid_operation())
176            .field("overflow", &self.overflow())
177            .field("clamped", &self.clamped())
178            .field("rounded", &self.rounded())
179            .field("subnormal", &self.subnormal())
180            .field("underflow", &self.underflow())
181            .field("raw", &self.inner)
182            .finish()
183    }
184}
185
186impl Status {
187    /// Reports whether any of the condition flags are set.
188    pub fn any(&self) -> bool {
189        self.inner != 0
190    }
191
192    /// Reports whether the conversion syntax flag is set.
193    ///
194    /// Operations set this flag when an invalid string is converted to a
195    /// decimal.
196    pub fn conversion_syntax(&self) -> bool {
197        self.inner & decnumber_sys::DEC_Conversion_syntax != 0
198    }
199
200    /// Sets `self`'s  conversion syntax flag.
201    pub fn set_conversion_syntax(&mut self) {
202        self.inner |= decnumber_sys::DEC_Conversion_syntax;
203    }
204
205    /// Reports whether the division by zero flag is set.
206    ///
207    /// Operations set this flag when a nonzero dividend is divided by zero.
208    pub fn division_by_zero(&self) -> bool {
209        self.inner & decnumber_sys::DEC_Division_by_zero != 0
210    }
211
212    /// Sets `self`'s division by zero flag.
213    pub fn set_division_by_zero(&mut self) {
214        self.inner |= decnumber_sys::DEC_Division_by_zero;
215    }
216
217    /// Reports whether the division impossible flag is set.
218    ///
219    /// Operations set this flag if the integer result of a division had too
220    /// many digits.
221    pub fn division_impossible(&self) -> bool {
222        self.inner & decnumber_sys::DEC_Division_impossible != 0
223    }
224
225    /// Sets `self`'s division impossible flag.
226    pub fn set_division_impossible(&mut self) {
227        self.inner |= decnumber_sys::DEC_Division_impossible;
228    }
229
230    /// Reports whether the division undefined flag is set.
231    ///
232    /// Operations set this flag when a zero dividend is divided by zero.
233    pub fn division_undefined(&self) -> bool {
234        self.inner & decnumber_sys::DEC_Division_undefined != 0
235    }
236
237    /// Sets `self`'s division undefined flag.
238    pub fn set_division_undefined(&mut self) {
239        self.inner |= decnumber_sys::DEC_Division_undefined;
240    }
241
242    /// Reports whether the insufficient storage flag is set.
243    ///
244    /// Operations set this flag if memory allocation fails.
245    pub fn insufficient_storage(&self) -> bool {
246        self.inner & decnumber_sys::DEC_Insufficient_storage != 0
247    }
248
249    /// Sets `self`'s insufficient storage flag.
250    pub fn set_insufficient_storage(&mut self) {
251        self.inner |= decnumber_sys::DEC_Insufficient_storage;
252    }
253
254    /// Reports whether the inexact flag is set.
255    ///
256    /// Operations set this flag when one or more nonzero coefficient digits
257    /// were discarded during rounding from a result.
258    pub fn inexact(&self) -> bool {
259        self.inner & decnumber_sys::DEC_Inexact != 0
260    }
261
262    /// Sets `self`'s inexact flag.
263    pub fn set_inexact(&mut self) {
264        self.inner |= decnumber_sys::DEC_Inexact;
265    }
266
267    /// Reports whether the invalid context flag is set.
268    ///
269    /// Operations set this flag if they detect an invalid context.
270    ///
271    /// It should not be possible to pass an invalid context to an operation
272    /// using this crate, but this method is nonetheless provided for
273    /// completeness.
274    pub fn invalid_context(&self) -> bool {
275        self.inner & decnumber_sys::DEC_Invalid_context != 0
276    }
277
278    /// Sets `self`'s invalid context flag.
279    pub fn set_invalid_context(&mut self) {
280        self.inner |= decnumber_sys::DEC_Invalid_context;
281    }
282
283    /// Reports whether the invalid operation flag is set.
284    ///
285    /// Various operations set this flag in response to invalid arguments.
286    pub fn invalid_operation(&self) -> bool {
287        self.inner & decnumber_sys::DEC_Invalid_operation != 0
288    }
289
290    /// Sets `self`'s invalid operation flag.
291    pub fn set_invalid_operation(&mut self) {
292        self.inner |= decnumber_sys::DEC_Invalid_operation;
293    }
294
295    /// Reports whether the overflow flag is set.
296    ///
297    /// Operations set this flag when the exponent of a result is too large to
298    /// be represented.
299    pub fn overflow(&self) -> bool {
300        self.inner & decnumber_sys::DEC_Overflow != 0
301    }
302
303    /// Sets `self`'s overflow flag.
304    pub fn set_overflow(&mut self) {
305        self.inner |= decnumber_sys::DEC_Overflow;
306    }
307
308    /// Reports whether the clamped flag is set.
309    ///
310    /// Operations set this flag when the exponent of a result has been altered
311    /// or constrained in order to fit the constraints of a specific concrete
312    /// representation.
313    pub fn clamped(&self) -> bool {
314        self.inner & decnumber_sys::DEC_Clamped != 0
315    }
316
317    /// Sets `self`'s clamped flag.
318    pub fn set_clamped(&mut self) {
319        self.inner |= decnumber_sys::DEC_Clamped;
320    }
321
322    /// Reports whether the rounded flag is set.
323    ///
324    /// Operations set this flag when one or more zero or nonzero coefficient
325    /// digits were discarded from a result.
326    pub fn rounded(&self) -> bool {
327        self.inner & decnumber_sys::DEC_Rounded != 0
328    }
329
330    /// Sets `self`'s rounded flag.
331    pub fn set_rounded(&mut self) {
332        self.inner |= decnumber_sys::DEC_Rounded;
333    }
334
335    /// Reports whether the subnormal flag is set.
336    ///
337    /// Operations set this flag when a result's adjusted exponent is less than
338    /// E<sub>min</sub> before any rounding.
339    pub fn subnormal(&self) -> bool {
340        self.inner & decnumber_sys::DEC_Subnormal != 0
341    }
342
343    /// Sets `self`'s subnormal flag.
344    pub fn set_subnormal(&mut self) {
345        self.inner |= decnumber_sys::DEC_Subnormal;
346    }
347
348    /// Reports whether the underflow flag is set.
349    ///
350    /// Operations set this flag when a result is both subnormal and inexact.
351    pub fn underflow(&self) -> bool {
352        self.inner & decnumber_sys::DEC_Underflow != 0
353    }
354
355    /// Sets `self`'s underflow flag.
356    pub fn set_underflow(&mut self) {
357        self.inner |= decnumber_sys::DEC_Underflow;
358    }
359}
360
361impl Default for Status {
362    fn default() -> Self {
363        Status { inner: 0 }
364    }
365}
366
367impl BitAnd for Status {
368    type Output = Status;
369
370    fn bitand(self, rhs: Status) -> Status {
371        Status {
372            inner: self.inner & rhs.inner,
373        }
374    }
375}
376
377impl BitAndAssign for Status {
378    fn bitand_assign(&mut self, rhs: Status) {
379        self.inner &= rhs.inner;
380    }
381}
382
383impl BitOr for Status {
384    type Output = Status;
385
386    fn bitor(self, rhs: Status) -> Status {
387        Status {
388            inner: self.inner | rhs.inner,
389        }
390    }
391}
392
393impl BitOrAssign for Status {
394    fn bitor_assign(&mut self, rhs: Status) {
395        self.inner |= rhs.inner;
396    }
397}
398
399/// The class of a decimal number.
400///
401/// These classes are precisely defined in [The Arithmetic Model][model] chapter
402/// of the General Decimal Arithmetic specification.
403///
404/// [model]: http://speleotrove.com/decimal/damodel.html
405#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
406pub enum Class {
407    /// Signaling NaN ("Not a Number").
408    SignalingNan,
409    /// Quiet NaN ("Not a Number").
410    QuietNan,
411    /// Negative infinity.
412    NegInfinity,
413    /// Negative normal.
414    NegNormal,
415    /// Negative subnormal.
416    NegSubnormal,
417    /// Negative zero.
418    NegZero,
419    /// Positive zero.
420    PosZero,
421    /// Positive subnormal.
422    PosSubnormal,
423    /// Positive normal.
424    PosNormal,
425    /// Positive infinity.
426    PosInfinity,
427}
428
429impl Class {
430    pub(crate) fn from_c(c: c_uint) -> Class {
431        match c {
432            decnumber_sys::DEC_CLASS_SNAN => Class::SignalingNan,
433            decnumber_sys::DEC_CLASS_QNAN => Class::QuietNan,
434            decnumber_sys::DEC_CLASS_NEG_INF => Class::NegInfinity,
435            decnumber_sys::DEC_CLASS_NEG_NORMAL => Class::NegNormal,
436            decnumber_sys::DEC_CLASS_NEG_SUBNORMAL => Class::NegSubnormal,
437            decnumber_sys::DEC_CLASS_NEG_ZERO => Class::NegZero,
438            decnumber_sys::DEC_CLASS_POS_ZERO => Class::PosZero,
439            decnumber_sys::DEC_CLASS_POS_SUBNORMAL => Class::PosSubnormal,
440            decnumber_sys::DEC_CLASS_POS_NORMAL => Class::PosNormal,
441            decnumber_sys::DEC_CLASS_POS_INF => Class::PosInfinity,
442            _ => unreachable!("invalid C class value"),
443        }
444    }
445}
446
447impl fmt::Display for Class {
448    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
449        match self {
450            Class::SignalingNan => f.write_str("sNaN"),
451            Class::QuietNan => f.write_str("NaN"),
452            Class::NegInfinity => f.write_str("-Infinity"),
453            Class::NegNormal => f.write_str("-Normal"),
454            Class::NegSubnormal => f.write_str("-Subnormal"),
455            Class::NegZero => f.write_str("-Zero"),
456            Class::PosZero => f.write_str("+Zero"),
457            Class::PosSubnormal => f.write_str("+Subnormal"),
458            Class::PosNormal => f.write_str("+Normal"),
459            Class::PosInfinity => f.write_str("+Infinity"),
460        }
461    }
462}