decimal_scaled/decimal_trait.rs
1//! The [`Decimal`] trait — the width-generic surface shared by every
2//! decimal type in this crate.
3//!
4//! Implemented by every width: `D9`, `D18`, `D38`, `D76`, `D153`, `D307`
5//! (each via the shared [`crate::macros::basics::decl_decimal_basics!`]
6//! macro). Lets downstream code write helpers that work across widths,
7//! e.g.
8//!
9//! ```ignore
10//! use decimal_scaled::Decimal;
11//!
12//! fn average<D: Decimal>(values: &[D]) -> D {
13//! D::sum(values.iter().copied()) / D::from_i32(values.len() as i32)
14//! }
15//! ```
16//!
17//! # Scope
18//!
19//! The trait carries the surface that has an identical *signature* on
20//! every width:
21//!
22//! - **Type information**: `Storage`, `SCALE`, `MAX_SCALE`.
23//! - **Constants**: `ZERO`, `ONE`, `MAX`, `MIN`, plus the
24//! `multiplier()` factor.
25//! - **Round-trip**: `from_bits` / `to_bits` / `scale`.
26//! - **Arithmetic operators**: as `Add` / `Sub` / `Mul` / `Div` /
27//! `Rem` / `Neg` supertrait bounds (also `*Assign`), reachable
28//! through plain `+` / `-` / etc.
29//! - **Bitwise operators**: `BitAnd` / `BitOr` / `BitXor` / `Not` /
30//! `Shl<u32>` / `Shr<u32>` (as supertrait bounds).
31//! - **Sign**: `abs`, `signum`, `is_positive`, `is_negative`.
32//! - **Integer methods**: `div_euclid`, `rem_euclid`, `div_floor`,
33//! `div_ceil`, `abs_diff`, `midpoint`, `mul_add`, plus the
34//! float-shape predicates `is_nan`, `is_infinite`, `is_finite`.
35//! - **Integer-exponent powers**: `pow`, `powi`, plus the four
36//! overflow-variant siblings (`checked_pow`, `wrapping_pow`,
37//! `saturating_pow`, `overflowing_pow`).
38//! - **Overflow-variant arithmetic**: `checked_*`, `wrapping_*`,
39//! `saturating_*`, `overflowing_*` of `add` / `sub` / `mul` / `div`
40//! / `rem` / `neg`.
41//! - **Integer conversion**: `from_i32`, `to_int`, `to_int_with`.
42//! - **Float bridge** (when `feature = "std"`): `from_f64`,
43//! `from_f64_with`, `to_f64`, `to_f32`.
44//! - **Default reductions**: `is_zero`, `is_one`, `is_normal`, `sum`,
45//! `product`.
46//!
47//! # Out of scope
48//!
49//! Some methods are deliberately not on the trait, because their
50//! signature varies per width or the trait can't represent it:
51//!
52//! - **Rescale** (`rescale<TARGET>` / `rescale_with`) takes a
53//! `const`-generic target `SCALE` parameter. Const-generic trait
54//! methods aren't stable.
55//! - **`from_int`** takes a different source integer per width
56//! (`i32` for `D9`, `i64` for `D18` / `D38`, `i128` for the wide
57//! tiers). Use [`Decimal::from_i32`] when you need a width-generic
58//! integer constructor, and accept the narrower input range.
59//! - **Transcendentals** (`ln` / `exp` / `sin` / …) are gated by the
60//! `strict` / `fast` features and live as inherent methods on the
61//! concrete types. Reach for those directly when needed.
62//! - **Mathematical constants** (`pi`, `tau`, `e`, …) live on
63//! [`crate::DecimalConsts`], a separate trait that every width also
64//! implements.
65//!
66//! For most users the concrete type (e.g. `D38<12>` or its alias
67//! `D38s12`) is the canonical surface. Reach for [`Decimal`] only when
68//! writing code that must work across widths.
69
70use crate::rounding::RoundingMode;
71
72/// Scaled fixed-point decimal type with a compile-time `SCALE` and a
73/// fixed-width integer `Storage`.
74///
75/// See the module-level documentation for the full surface and what's
76/// intentionally not on the trait.
77///
78/// # Precision
79///
80/// N/A: this is a trait definition; no arithmetic is performed.
81pub trait Decimal:
82 Copy
83 + PartialEq
84 + Eq
85 + PartialOrd
86 + Ord
87 + Default
88 + core::fmt::Debug
89 + core::fmt::Display
90 + core::hash::Hash
91 + core::ops::Add<Output = Self>
92 + core::ops::Sub<Output = Self>
93 + core::ops::Mul<Output = Self>
94 + core::ops::Div<Output = Self>
95 + core::ops::Rem<Output = Self>
96 + core::ops::Neg<Output = Self>
97 + core::ops::AddAssign
98 + core::ops::SubAssign
99 + core::ops::MulAssign
100 + core::ops::DivAssign
101 + core::ops::RemAssign
102 + core::ops::BitAnd<Output = Self>
103 + core::ops::BitOr<Output = Self>
104 + core::ops::BitXor<Output = Self>
105 + core::ops::Not<Output = Self>
106 + core::ops::Shl<u32, Output = Self>
107 + core::ops::Shr<u32, Output = Self>
108{
109 /// Underlying integer storage type (e.g. `i128` for `D38<SCALE>`).
110 type Storage: Copy + PartialEq + Eq;
111
112 /// The decimal scale of this type, equal to the const-generic
113 /// parameter. One LSB of storage represents `10^-SCALE`.
114 const SCALE: u32;
115
116 /// The maximum legal `SCALE` for this width. Equal to the largest
117 /// `k` such that `10^k` fits in `Self::Storage`. For example, 38
118 /// for `D38`, 76 for `D76`.
119 const MAX_SCALE: u32;
120
121 /// The additive identity (logical value `0`).
122 const ZERO: Self;
123
124 /// The multiplicative identity (logical value `1`).
125 const ONE: Self;
126
127 /// The largest representable value (storage equal to
128 /// `Self::Storage::MAX`).
129 const MAX: Self;
130
131 /// The smallest representable value (storage equal to
132 /// `Self::Storage::MIN`).
133 const MIN: Self;
134
135 /// Returns `10^SCALE`, the factor that converts a logical integer
136 /// to its storage representation.
137 fn multiplier() -> Self::Storage;
138
139 /// Constructs from a raw storage value.
140 fn from_bits(raw: Self::Storage) -> Self;
141
142 /// Returns the raw storage value.
143 fn to_bits(self) -> Self::Storage;
144
145 /// Returns the decimal scale of this value (equal to
146 /// [`Self::SCALE`]; provided for ergonomic method-call syntax).
147 fn scale(self) -> u32;
148
149 // ── Sign ──────────────────────────────────────────────────────────
150
151 /// Absolute value. `Self::MIN.abs()` panics in debug, wraps to
152 /// `Self::MIN` in release (mirroring the storage type).
153 fn abs(self) -> Self;
154
155 /// `+ONE`, `ZERO`, or `-ONE` according to the sign of `self`.
156 fn signum(self) -> Self;
157
158 /// `true` when `self > ZERO`.
159 fn is_positive(self) -> bool;
160
161 /// `true` when `self < ZERO`.
162 fn is_negative(self) -> bool;
163
164 // ── Integer-shape predicates ─────────────────────────────────────
165 //
166 // A fixed-point decimal is always a finite, non-NaN integer in its
167 // storage representation. These predicates exist so generic
168 // numeric code that branches on the float predicates also works
169 // here; they return constants.
170
171 /// Always `false` — fixed-point decimals cannot represent NaN.
172 fn is_nan(self) -> bool;
173
174 /// Always `false` — fixed-point decimals cannot represent infinity.
175 fn is_infinite(self) -> bool;
176
177 /// Always `true` — every fixed-point decimal value is finite.
178 fn is_finite(self) -> bool;
179
180 // ── Integer methods ──────────────────────────────────────────────
181
182 /// Euclidean division: integer quotient (rounded so the remainder
183 /// is non-negative) scaled back into `Self`. Panics on `rhs == ZERO`.
184 fn div_euclid(self, rhs: Self) -> Self;
185
186 /// Euclidean remainder, non-negative when `rhs != ZERO`. Panics on
187 /// `rhs == ZERO`.
188 fn rem_euclid(self, rhs: Self) -> Self;
189
190 /// Floor-rounded division (toward `-∞`). Panics on `rhs == ZERO`.
191 fn div_floor(self, rhs: Self) -> Self;
192
193 /// Ceil-rounded division (toward `+∞`). Panics on `rhs == ZERO`.
194 fn div_ceil(self, rhs: Self) -> Self;
195
196 /// `|self - rhs|`, computed without intermediate overflow.
197 fn abs_diff(self, rhs: Self) -> Self;
198
199 /// Midpoint of `self` and `rhs` (rounding toward `-∞`), computed
200 /// without intermediate overflow.
201 fn midpoint(self, rhs: Self) -> Self;
202
203 /// `self * a + b` — mirrors the `f64::mul_add` call shape so
204 /// f64-generic numeric code can monomorphise to a decimal type.
205 fn mul_add(self, a: Self, b: Self) -> Self;
206
207 // ── Integer-exponent powers ──────────────────────────────────────
208
209 /// `self^exp` via square-and-multiply. Overflow follows `Mul`
210 /// (debug panic, release wrap).
211 fn pow(self, exp: u32) -> Self;
212
213 /// Signed integer exponent. Negative `exp` produces
214 /// `ONE / self.pow(|exp|)`; the divide truncates at the type's
215 /// scale.
216 fn powi(self, exp: i32) -> Self;
217
218 /// `Some(self^exp)`, or `None` if any intermediate step overflows.
219 fn checked_pow(self, exp: u32) -> Option<Self>;
220
221 /// Wrapping `pow`. Each multiplication step wraps in the storage
222 /// type.
223 fn wrapping_pow(self, exp: u32) -> Self;
224
225 /// Saturating `pow` — clamps to `MAX` / `MIN` on overflow, with
226 /// the sign matching the mathematical result.
227 fn saturating_pow(self, exp: u32) -> Self;
228
229 /// `(self^exp, overflowed)` — the wrapping result paired with a
230 /// boolean.
231 fn overflowing_pow(self, exp: u32) -> (Self, bool);
232
233 // ── Overflow-variant arithmetic ─────────────────────────────────
234
235 /// `Some(self + rhs)`, or `None` if the sum overflows.
236 fn checked_add(self, rhs: Self) -> Option<Self>;
237 /// `Some(self - rhs)`, or `None` if the difference overflows.
238 fn checked_sub(self, rhs: Self) -> Option<Self>;
239 /// `Some(self * rhs)`, or `None` if the scaled product overflows.
240 fn checked_mul(self, rhs: Self) -> Option<Self>;
241 /// `Some(self / rhs)`, or `None` if `rhs == ZERO` or the quotient
242 /// overflows the storage.
243 fn checked_div(self, rhs: Self) -> Option<Self>;
244 /// `Some(-self)`, or `None` when `self == MIN`.
245 fn checked_neg(self) -> Option<Self>;
246 /// `Some(self % rhs)`, or `None` on divide-by-zero / `MIN % -ONE`.
247 fn checked_rem(self, rhs: Self) -> Option<Self>;
248
249 /// Two's-complement wrapping `+`.
250 fn wrapping_add(self, rhs: Self) -> Self;
251 /// Two's-complement wrapping `-`.
252 fn wrapping_sub(self, rhs: Self) -> Self;
253 /// Wrapping `*` — intermediate widens for overflow detection, the
254 /// final narrowing wraps.
255 fn wrapping_mul(self, rhs: Self) -> Self;
256 /// Wrapping `/` — **panics on `rhs == ZERO`**, matching
257 /// `i128::wrapping_div`.
258 fn wrapping_div(self, rhs: Self) -> Self;
259 /// Wrapping `-self`; `MIN.wrapping_neg() == MIN`.
260 fn wrapping_neg(self) -> Self;
261 /// Wrapping `%` — **panics on `rhs == ZERO`**.
262 fn wrapping_rem(self, rhs: Self) -> Self;
263
264 /// Saturating `+`.
265 fn saturating_add(self, rhs: Self) -> Self;
266 /// Saturating `-`.
267 fn saturating_sub(self, rhs: Self) -> Self;
268 /// Saturating `*` — sign of the saturated bound matches the
269 /// mathematical product.
270 fn saturating_mul(self, rhs: Self) -> Self;
271 /// Saturating `/` — divide-by-zero saturates to `MAX` / `MIN`
272 /// according to the sign of `self`.
273 fn saturating_div(self, rhs: Self) -> Self;
274 /// Saturating `-self` — `MIN.saturating_neg() == MAX`.
275 fn saturating_neg(self) -> Self;
276
277 /// Overflowing `+`.
278 fn overflowing_add(self, rhs: Self) -> (Self, bool);
279 /// Overflowing `-`.
280 fn overflowing_sub(self, rhs: Self) -> (Self, bool);
281 /// Overflowing `*`.
282 fn overflowing_mul(self, rhs: Self) -> (Self, bool);
283 /// Overflowing `/` — `overflowed` is `true` on out-of-range or
284 /// divide-by-zero.
285 fn overflowing_div(self, rhs: Self) -> (Self, bool);
286 /// Overflowing `-self` — `overflowed` is `true` iff `self == MIN`.
287 fn overflowing_neg(self) -> (Self, bool);
288 /// Overflowing `%`.
289 fn overflowing_rem(self, rhs: Self) -> (Self, bool);
290
291 // ── Integer conversion ───────────────────────────────────────────
292
293 /// Construct from an `i32`, scaling by `10^SCALE`. Width-generic
294 /// integer constructor; for wider source integers use the
295 /// concrete type's `from_int` (whose `IntSrc` parameter varies
296 /// per width).
297 fn from_i32(value: i32) -> Self;
298
299 /// Convert to `i64` using the crate-default rounding mode, with
300 /// saturating overflow on out-of-range integer parts.
301 fn to_int(self) -> i64;
302
303 /// Convert to `i64` using the supplied rounding mode for the
304 /// fractional discard step. Saturating overflow on out-of-range
305 /// integer parts.
306 fn to_int_with(self, mode: RoundingMode) -> i64;
307
308 // ── Float bridge (lossy) ─────────────────────────────────────────
309
310 /// Construct from `f64` using the crate-default rounding mode.
311 /// `NaN` saturates to `ZERO`; `±∞` saturates to `MAX` / `MIN`.
312 #[cfg(feature = "std")]
313 fn from_f64(value: f64) -> Self;
314
315 /// Construct from `f64` using the supplied rounding mode.
316 #[cfg(feature = "std")]
317 fn from_f64_with(value: f64, mode: RoundingMode) -> Self;
318
319 /// Convert to `f64`. Lossy when the storage magnitude exceeds
320 /// `f64`'s ~15-digit exact range.
321 #[cfg(feature = "std")]
322 fn to_f64(self) -> f64;
323
324 /// Convert to `f32`. Lossy.
325 #[cfg(feature = "std")]
326 fn to_f32(self) -> f32;
327
328 // ── Default reductions ───────────────────────────────────────────
329
330 /// `true` if this value is the additive identity.
331 #[inline]
332 fn is_zero(self) -> bool {
333 self == Self::ZERO
334 }
335
336 /// `true` if this value is the multiplicative identity.
337 #[inline]
338 fn is_one(self) -> bool {
339 self == Self::ONE
340 }
341
342 /// `true` for every non-zero value (a fixed-point decimal has no
343 /// subnormals).
344 #[inline]
345 fn is_normal(self) -> bool {
346 !self.is_zero()
347 }
348
349 /// Sums an iterator of decimals of this width, starting from
350 /// `ZERO`. Width-generic convenience for `iter.fold(ZERO, +)`.
351 #[inline]
352 fn sum<I>(iter: I) -> Self
353 where
354 I: IntoIterator<Item = Self>,
355 {
356 iter.into_iter().fold(Self::ZERO, |acc, x| acc + x)
357 }
358
359 /// Multiplies an iterator of decimals of this width, starting from
360 /// `ONE`. Width-generic convenience for `iter.fold(ONE, *)`.
361 #[inline]
362 fn product<I>(iter: I) -> Self
363 where
364 I: IntoIterator<Item = Self>,
365 {
366 iter.into_iter().fold(Self::ONE, |acc, x| acc * x)
367 }
368}
369
370// The `Decimal` trait impl for every width is emitted by the
371// `decl_decimal_basics!` macro in `src/macros/basics.rs`.