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}