radix_common/math/
rounding_mode.rs

1#[cfg(feature = "fuzzing")]
2use arbitrary::Arbitrary;
3use core::cmp::Ordering;
4use sbor::Sbor;
5#[cfg(feature = "fuzzing")]
6use serde::{Deserialize, Serialize};
7
8/// Defines the rounding strategy.
9///
10/// Following the same naming convention as https://docs.rs/rust_decimal/latest/rust_decimal/enum.RoundingStrategy.html.
11#[cfg_attr(feature = "fuzzing", derive(Arbitrary, Serialize, Deserialize))]
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Sbor)]
13pub enum RoundingMode {
14    /// The number is always rounded toward positive infinity, e.g. `3.1 -> 4`, `-3.1 -> -3`.
15    ToPositiveInfinity,
16    /// The number is always rounded toward negative infinity, e.g. `3.1 -> 3`, `-3.1 -> -4`.
17    ToNegativeInfinity,
18    /// The number is always rounded toward zero, e.g. `3.1 -> 3`, `-3.1 -> -3`.
19    ToZero,
20    /// The number is always rounded away from zero, e.g. `3.1 -> 4`, `-3.1 -> -4`.
21    AwayFromZero,
22
23    /// 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`.
24    ToNearestMidpointTowardZero,
25    /// 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`.
26    ToNearestMidpointAwayFromZero,
27    /// 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".
28    ToNearestMidpointToEven,
29}
30
31/// The resolved rounding strategy internal to the round method
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}