radix_common/math/
rounding_mode.rs

1use core::cmp::Ordering;
2use sbor::Sbor;
3
4/// Defines the rounding strategy.
5///
6/// Following the same naming convention as https://docs.rs/rust_decimal/latest/rust_decimal/enum.RoundingStrategy.html.
7#[cfg_attr(
8    feature = "fuzzing",
9    derive(::arbitrary::Arbitrary, ::serde::Serialize, ::serde::Deserialize)
10)]
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Sbor)]
12pub enum RoundingMode {
13    /// The number is always rounded toward positive infinity, e.g. `3.1 -> 4`, `-3.1 -> -3`.
14    ToPositiveInfinity,
15    /// The number is always rounded toward negative infinity, e.g. `3.1 -> 3`, `-3.1 -> -4`.
16    ToNegativeInfinity,
17    /// The number is always rounded toward zero, e.g. `3.1 -> 3`, `-3.1 -> -3`.
18    ToZero,
19    /// The number is always rounded away from zero, e.g. `3.1 -> 4`, `-3.1 -> -4`.
20    AwayFromZero,
21
22    /// The number is rounded to the nearest, and when it is halfway between two others, it's rounded toward zero, e.g. `3.5 -> 3`, `-3.5 -> -3`.
23    ToNearestMidpointTowardZero,
24    /// The number is rounded to the nearest, and when it is halfway between two others, it's rounded away from zero, e.g. `3.5 -> 4`, `-3.5 -> -4`.
25    ToNearestMidpointAwayFromZero,
26    /// The number is rounded to the nearest, and when it is halfway between two others, it's rounded toward the nearest even number. Also known as "Bankers Rounding".
27    ToNearestMidpointToEven,
28}
29
30/// The resolved rounding strategy internal to the round method
31#[allow(clippy::enum_variant_names)]
32pub(crate) enum ResolvedRoundingStrategy {
33    RoundUp,
34    RoundDown,
35    RoundToEven,
36}
37
38impl ResolvedRoundingStrategy {
39    pub fn from_mode(
40        mode: RoundingMode,
41        is_positive: bool,
42        compare_to_midpoint: impl FnOnce() -> Ordering,
43    ) -> Self {
44        match mode {
45            RoundingMode::ToPositiveInfinity => ResolvedRoundingStrategy::RoundUp,
46            RoundingMode::ToNegativeInfinity => ResolvedRoundingStrategy::RoundDown,
47            RoundingMode::ToZero => ResolvedRoundingStrategy::towards_zero(is_positive),
48            RoundingMode::AwayFromZero => ResolvedRoundingStrategy::away_from_zero(is_positive),
49            RoundingMode::ToNearestMidpointTowardZero => Self::from_midpoint_ordering(
50                compare_to_midpoint(),
51                ResolvedRoundingStrategy::towards_zero(is_positive),
52            ),
53            RoundingMode::ToNearestMidpointAwayFromZero => Self::from_midpoint_ordering(
54                compare_to_midpoint(),
55                ResolvedRoundingStrategy::away_from_zero(is_positive),
56            ),
57            RoundingMode::ToNearestMidpointToEven => Self::from_midpoint_ordering(
58                compare_to_midpoint(),
59                ResolvedRoundingStrategy::RoundToEven,
60            ),
61        }
62    }
63
64    fn from_midpoint_ordering(ordering: Ordering, equal_strategy: Self) -> Self {
65        match ordering {
66            Ordering::Less => Self::RoundDown,
67            Ordering::Equal => equal_strategy,
68            Ordering::Greater => Self::RoundUp,
69        }
70    }
71
72    fn towards_zero(is_positive: bool) -> Self {
73        if is_positive {
74            Self::RoundDown
75        } else {
76            Self::RoundUp
77        }
78    }
79
80    fn away_from_zero(is_positive: bool) -> Self {
81        if is_positive {
82            Self::RoundUp
83        } else {
84            Self::RoundDown
85        }
86    }
87}