Skip to main content

fastnum/decimal/
signals.rs

1use core::fmt::{Debug, Display, Formatter};
2
3use crate::utils::assert_eq_size;
4
5/// # Signals
6///
7/// The exceptional conditions are grouped into signals, which can be controlled
8/// individually.
9/// For each of the signals, the corresponding flag is set to `1`
10/// when the signal occurs.
11#[derive(Copy, Clone, Hash, PartialEq, Eq)]
12#[repr(transparent)]
13pub struct Signals(u8);
14
15impl Signals {
16    pub(crate) const OP_CLAMPED_MASK: u8 = 0b0000_0001;
17    pub(crate) const OP_DIV_BY_ZERO_MASK: u8 = 0b0000_0010;
18    pub(crate) const OP_INVALID_MASK: u8 = 0b0000_0100;
19    pub(crate) const OP_INEXACT_MASK: u8 = 0b0000_1000;
20    pub(crate) const OP_OVERFLOW_MASK: u8 = 0b0001_0000;
21    pub(crate) const OP_ROUNDED_MASK: u8 = 0b0010_0000;
22    pub(crate) const OP_SUBNORMAL_MASK: u8 = 0b0100_0000;
23    pub(crate) const OP_UNDERFLOW_MASK: u8 = 0b1000_0000;
24
25    /// By default, no operation signals are raised.
26    pub const EMPTY: Self = Self(0);
27
28    /// Raised when the exponent of a result has been altered or constrained to
29    /// fit the target type.
30    pub const OP_CLAMPED: Self = Self(Self::OP_CLAMPED_MASK);
31
32    /// Raised when a non-zero dividend is divided by zero.
33    pub const OP_DIV_BY_ZERO: Self = Self(Self::OP_DIV_BY_ZERO_MASK);
34
35    /// Raised when a result would be undefined or impossible.
36    pub const OP_INVALID: Self = Self(Self::OP_INVALID_MASK);
37
38    /// Raised when a result is not exact (one or more non-zero coefficient
39    /// digits were discarded during rounding).
40    pub const OP_INEXACT: Self = Self(Self::OP_INEXACT_MASK);
41
42    /// Raised when the exponent of a result is too large to be represented.
43    pub const OP_OVERFLOW: Self = Self(Self::OP_OVERFLOW_MASK);
44
45    /// Raised when a result has been rounded (that is, some zero or non-zero
46    /// coefficient digits were discarded).
47    pub const OP_ROUNDED: Self = Self(Self::OP_ROUNDED_MASK);
48
49    /// Raised when a result is subnormal (its adjusted exponent is less than
50    /// E<sub>min</sub>), before any rounding.
51    pub const OP_SUBNORMAL: Self = Self(Self::OP_SUBNORMAL_MASK);
52
53    /// Raised when a result is both subnormal and inexact.
54    pub const OP_UNDERFLOW: Self = Self(Self::OP_UNDERFLOW_MASK);
55
56    pub(crate) const DEFAULT_TRAPS: Self =
57        Self(Self::OP_DIV_BY_ZERO.0 | Self::OP_INVALID.0 | Self::OP_OVERFLOW.0);
58
59    /// Return an empty set of signaling flags.
60    #[must_use]
61    #[inline(always)]
62    pub const fn empty() -> Self {
63        Self::EMPTY
64    }
65
66    #[inline(always)]
67    pub(crate) const fn new(mask: u8) -> Self {
68        Self(mask)
69    }
70
71    #[inline(always)]
72    pub(crate) const fn mask(&self) -> u8 {
73        self.0
74    }
75
76    #[inline(always)]
77    pub(crate) const fn raise(&mut self, signals: Signals) {
78        *self = self.combine(signals);
79    }
80
81    /// Combines the given signal with another one.
82    #[must_use]
83    #[inline(always)]
84    pub const fn combine(mut self, other: Self) -> Self {
85        self.0 |= other.0;
86        self
87    }
88
89    /// Intersect the given signal with another one.
90    #[must_use]
91    #[inline(always)]
92    pub const fn intersect(mut self, other: Self) -> Self {
93        self.0 &= other.0;
94        self
95    }
96
97    #[must_use]
98    #[inline(always)]
99    pub(crate) const fn set(mut self, other: Self) -> Self {
100        self.0 |= other.0;
101        self
102    }
103
104    #[allow(dead_code)]
105    #[must_use]
106    #[inline(always)]
107    pub(crate) const fn unset(mut self, other: Self) -> Self {
108        self.0 &= !other.0;
109        self
110    }
111
112    #[allow(dead_code)]
113    #[must_use]
114    #[inline(always)]
115    pub(crate) const fn toggle(mut self, other: Self) -> Self {
116        self.0 ^= other.0;
117        self
118    }
119
120    /// Is empty
121    #[must_use]
122    #[inline(always)]
123    pub const fn is_empty(&self) -> bool {
124        self.0 == Self::EMPTY.0
125    }
126
127    #[must_use]
128    #[inline(always)]
129    pub(crate) const fn is_raised(&self, other: Self) -> bool {
130        self.0 & other.0 != 0
131    }
132}
133
134macro_rules! display {
135    ($self: ident, $f: ident, $($v: ident => $l: literal),*) => {
136        #[allow(unused_assignments)]
137        {
138            let mut delimiter = false;
139            $(
140                if $self.is_raised(Self::$v) {
141                    match delimiter {
142                        true => {
143                            write!($f, ", ")?;
144                        }
145                        false => {
146                            delimiter = true;
147                        }
148                    }
149                    write!($f, $l)?;
150                }
151            )*
152        }
153    };
154}
155
156impl Display for Signals {
157    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
158        if self.is_empty() {
159            return Ok(());
160        }
161
162        display!(self, f,
163            OP_CLAMPED => "!CP",
164            OP_DIV_BY_ZERO => "!DBZ",
165            OP_INEXACT => "!INEXACT",
166            OP_INVALID => "!INV",
167            OP_OVERFLOW => "!OFW",
168            OP_ROUNDED => "!ROUND",
169            OP_SUBNORMAL => "!SN",
170            OP_UNDERFLOW => "!UFW"
171        );
172
173        Ok(())
174    }
175}
176
177impl Debug for Signals {
178    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
179        write!(f, "{self}")
180    }
181}
182
183assert_eq_size!(Signals, u8);