fixed_decimal/
rounding.rs

1// This file is part of ICU4X. For terms of use, please see the file
2// called LICENSE at the top level of the ICU4X source tree
3// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
4
5//! This file contains the implementation of the rounding algorithm and the related functions & types.
6
7// Adapters to convert runtime dispatched calls into const-inlined methods.
8// This allows reducing the codesize for the common case of no increment.
9
10#[derive(Copy, Clone, PartialEq)]
11pub(crate) struct NoIncrement;
12
13pub(crate) trait IncrementLike: Copy + Sized + PartialEq {
14    const MULTIPLES_OF_1: Option<Self>;
15    const MULTIPLES_OF_2: Option<Self>;
16    const MULTIPLES_OF_5: Option<Self>;
17    const MULTIPLES_OF_25: Option<Self>;
18}
19
20impl IncrementLike for RoundingIncrement {
21    const MULTIPLES_OF_1: Option<Self> = Some(Self::MultiplesOf1);
22
23    const MULTIPLES_OF_2: Option<Self> = Some(Self::MultiplesOf2);
24
25    const MULTIPLES_OF_5: Option<Self> = Some(Self::MultiplesOf5);
26
27    const MULTIPLES_OF_25: Option<Self> = Some(Self::MultiplesOf25);
28}
29
30impl IncrementLike for NoIncrement {
31    const MULTIPLES_OF_1: Option<Self> = Some(Self);
32
33    const MULTIPLES_OF_2: Option<Self> = None;
34
35    const MULTIPLES_OF_5: Option<Self> = None;
36
37    const MULTIPLES_OF_25: Option<Self> = None;
38}
39
40/// Mode used in a unsigned rounding operations.
41///
42/// # Comparative table of all the rounding modes, including the signed and unsigned ones.
43///
44/// | Value | Ceil | Expand | Floor | Trunc | HalfCeil | HalfExpand | HalfFloor | HalfTrunc | HalfEven |
45/// |:-----:|:----:|:------:|:-----:|:-----:|:--------:|:----------:|:---------:|:---------:|:--------:|
46/// |  +1.8 |  +2  |   +2   |   +1  |   +1  |    +2    |     +2     |     +2    |     +2    |    +2    |
47/// |  +1.5 |   "  |    "   |   "   |   "   |     "    |      "     |     +1    |     +1    |     "    |
48/// |  +1.2 |   "  |    "   |   "   |   "   |    +1    |     +1     |     "     |     "     |    +1    |
49/// |  +0.8 |  +1  |   +1   |   0   |   0   |     "    |      "     |     "     |     "     |     "    |
50/// |  +0.5 |   "  |    "   |   "   |   "   |     "    |      "     |     0     |     0     |     0    |
51/// |  +0.2 |   "  |    "   |   "   |   "   |     0    |      0     |     "     |     "     |     "    |
52/// |  -0.2 |   0  |   -1   |   -1  |   "   |     "    |      "     |     "     |     "     |     "    |
53/// |  -0.5 |   "  |    "   |   "   |   "   |     "    |     -1     |     -1    |     "     |     "    |
54/// |  -0.8 |   "  |    "   |   "   |   "   |    -1    |      "     |     "     |     -1    |    -1    |
55/// |  -1.2 |  -1  |   -2   |   -2  |   -1  |     "    |      "     |     "     |     "     |     "    |
56/// |  -1.5 |   "  |    "   |   "   |   "   |     "    |     -2     |     -2    |     "     |    -2    |
57/// |  -1.8 |   "  |    "   |   "   |   "   |    -2    |      "     |     "     |     -2    |     "    |
58///
59/// NOTE:
60///   - Ceil, Floor, HalfCeil and HalfFloor are part of the [`SignedRoundingMode`] enum.
61#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
62#[non_exhaustive]
63pub enum UnsignedRoundingMode {
64    Expand,
65    Trunc,
66    HalfExpand,
67    HalfTrunc,
68    HalfEven,
69}
70
71/// Mode used in a signed rounding operations.
72///
73/// NOTE:
74///   - You can find the comparative table of all the rounding modes in the [`UnsignedRoundingMode`] documentation.
75#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
76#[non_exhaustive]
77pub enum SignedRoundingMode {
78    Unsigned(UnsignedRoundingMode),
79    Ceil,
80    Floor,
81    HalfCeil,
82    HalfFloor,
83}
84
85/// Increment used in a rounding operation.
86///
87/// Forces a rounding operation to round to only multiples of the specified increment.
88///
89/// # Example
90///
91/// ```
92/// use fixed_decimal::{Decimal, RoundingIncrement, SignedRoundingMode, UnsignedRoundingMode};
93/// use writeable::assert_writeable_eq;
94/// # use std::str::FromStr;
95/// let dec = Decimal::from_str("-7.266").unwrap();
96/// let mode = SignedRoundingMode::Unsigned(UnsignedRoundingMode::Expand);
97/// let increments = [
98///     // .266 normally expands to .27 when rounding on position -2...
99///     (RoundingIncrement::MultiplesOf1, "-7.27"),
100///     // ...however, when rounding to multiples of 2, .266 expands to .28, since the next multiple
101///     // of 2 bigger than the least significant digit of the rounded value (7) is 8.
102///     (RoundingIncrement::MultiplesOf2, "-7.28"),
103///     // .266 expands to .30, since the next multiple of 5 bigger than 7 is 10.
104///     (RoundingIncrement::MultiplesOf5, "-7.30"),
105///     // .266 expands to .50, since the next multiple of 25 bigger than 27 is 50.
106///     // Note how we compare against 27 instead of only 7, because the increment applies to
107///     // the two least significant digits of the rounded value instead of only the least
108///     // significant digit.
109///     (RoundingIncrement::MultiplesOf25, "-7.50"),
110/// ];
111///
112/// for (increment, expected) in increments {
113///     assert_writeable_eq!(
114///         dec.clone().rounded_with_mode_and_increment(
115///             -2,
116///             mode,
117///             increment
118///         ),
119///         expected
120///     );
121/// }
122/// ```
123#[derive(Debug, Eq, PartialEq, Clone, Copy, Default)]
124#[non_exhaustive]
125pub enum RoundingIncrement {
126    /// Round the least significant digit to any digit (0-9).
127    ///
128    /// This is the default rounding increment for all the methods that don't take a
129    /// `RoundingIncrement` as an argument.
130    #[default]
131    MultiplesOf1,
132    /// Round the least significant digit to multiples of two (0, 2, 4, 6, 8).
133    MultiplesOf2,
134    /// Round the least significant digit to multiples of five (0, 5).
135    MultiplesOf5,
136    /// Round the two least significant digits to multiples of twenty-five (0, 25, 50, 75).
137    ///
138    /// With this increment, the rounding position index will match the least significant digit
139    /// of the multiple of 25; e.g. the number .264 expanded at position -2 using increments of 25
140    /// will give .50 as a result, since the next multiple of 25 bigger than 26 is 50.
141    MultiplesOf25,
142}
143
144/// Specifies the precision of a floating point value when constructing a FixedDecimal.
145///
146/// IEEE 754 is a representation of a point on the number line. On the other hand, FixedDecimal
147/// specifies not only the point on the number line but also the precision of the number to a
148/// specific power of 10. This enum augments a floating-point value with the additional
149/// information required by FixedDecimal.
150#[non_exhaustive]
151#[cfg(feature = "ryu")]
152#[derive(Debug, Clone, Copy)]
153pub enum FloatPrecision {
154    /// Specify that the floating point number is integer-valued.
155    ///
156    /// If the floating point is not actually integer-valued, an error will be returned.
157    Integer,
158
159    /// Specify that the floating point number is precise to a specific power of 10.
160    /// The number may be rounded or trailing zeros may be added as necessary.
161    Magnitude(i16),
162
163    /// Specify that the floating point number is precise to a specific number of significant digits.
164    /// The number may be rounded or trailing zeros may be added as necessary.
165    ///
166    /// The number requested may not be zero
167    SignificantDigits(u8),
168
169    /// Specify that the floating point number is precise to the maximum representable by IEEE.
170    ///
171    /// This results in a FixedDecimal having enough digits to recover the original floating point
172    /// value, with no trailing zeros.
173    RoundTrip,
174}