Skip to main content

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`.