1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
//! Macro-generated integer-flavoured methods shared by every decimal
//! width: Euclidean division / remainder, floor / ceil division,
//! absolute difference, midpoint, and the `is_zero` predicate.
//!
//! `div_euclid` / `rem_euclid` / `abs_diff` / `midpoint` are spelled
//! identically for primitive-integer and wide storage and live in a
//! shared `@common` arm. `div_floor` / `div_ceil` / `is_zero` compare
//! against the integer literal `0`, which a wide-integer value cannot, so
//! they are written per front-end arm.
/// Emits `div_euclid`, `rem_euclid`, `div_floor`, `div_ceil`,
/// `abs_diff`, `midpoint`, and `is_zero` for a decimal type.
///
/// - `decl_decimal_int_methods!(D38, i128)` — *native* storage.
/// - `decl_decimal_int_methods!(wide D76, I256)` — *wide* storage.
macro_rules! decl_decimal_int_methods {
// Wide storage.
(wide $Type:ident, $Storage:ty) => {
$crate::macros::int_methods::decl_decimal_int_methods!(@common $Type, $Storage);
impl<const SCALE: u32> $Type<SCALE> {
/// Floor-rounded division: `floor(self / rhs)` as an integer
/// multiple of `ONE`. Panics on `rhs == ZERO`.
#[inline]
#[must_use]
pub fn div_floor(self, rhs: Self) -> Self {
let q = self.0 / rhs.0;
let r = self.0 % rhs.0;
let zero = <$Storage>::from_str_radix("0", 10)
.expect("wide decimal: invalid base-10 literal");
let one = <$Storage>::from_str_radix("1", 10)
.expect("wide decimal: invalid base-10 literal");
let raw = if r != zero && (r ^ rhs.0).is_negative() {
q - one
} else {
q
};
Self(raw * Self::multiplier())
}
/// Ceil-rounded division: `ceil(self / rhs)` as an integer
/// multiple of `ONE`. Panics on `rhs == ZERO`.
#[inline]
#[must_use]
pub fn div_ceil(self, rhs: Self) -> Self {
let q = self.0 / rhs.0;
let r = self.0 % rhs.0;
let zero = <$Storage>::from_str_radix("0", 10)
.expect("wide decimal: invalid base-10 literal");
let one = <$Storage>::from_str_radix("1", 10)
.expect("wide decimal: invalid base-10 literal");
let raw = if r != zero && !(r ^ rhs.0).is_negative() {
q + one
} else {
q
};
Self(raw * Self::multiplier())
}
/// `true` if `self` is the additive identity.
#[inline]
#[must_use]
pub fn is_zero(self) -> bool {
self == Self::ZERO
}
/// Returns `true` for any non-zero value. A fixed-point
/// decimal has no subnormals, so zero is the only value that
/// is not "normal".
#[inline]
#[must_use]
pub fn is_normal(self) -> bool {
self != Self::ZERO
}
}
};
// Native (primitive integer) storage.
($Type:ident, $Storage:ty) => {
$crate::macros::int_methods::decl_decimal_int_methods!(@common $Type, $Storage);
impl<const SCALE: u32> $Type<SCALE> {
/// Floor-rounded division: `floor(self / rhs)` as an integer
/// multiple of `ONE`. Panics on `rhs == ZERO`.
///
/// Inlined rather than using `i128::div_floor`, which is
/// still unstable for signed types.
#[inline]
#[must_use]
pub fn div_floor(self, rhs: Self) -> Self {
let q = self.0 / rhs.0;
let r = self.0 % rhs.0;
// XOR of sign bits detects a remainder/divisor sign mismatch.
let raw = if r != 0 && (r ^ rhs.0) < 0 { q - 1 } else { q };
Self(raw * Self::multiplier())
}
/// Ceil-rounded division: `ceil(self / rhs)` as an integer
/// multiple of `ONE`. Panics on `rhs == ZERO`.
#[inline]
#[must_use]
pub fn div_ceil(self, rhs: Self) -> Self {
let q = self.0 / rhs.0;
let r = self.0 % rhs.0;
let raw = if r != 0 && (r ^ rhs.0) >= 0 { q + 1 } else { q };
Self(raw * Self::multiplier())
}
/// `true` if `self` is the additive identity.
#[inline]
#[must_use]
pub const fn is_zero(self) -> bool {
self.0 == 0
}
/// Returns `true` for any non-zero value. A fixed-point
/// decimal has no subnormals, so zero is the only value that
/// is not "normal".
#[inline]
#[must_use]
pub const fn is_normal(self) -> bool {
self.0 != 0
}
}
};
// Shared: div_euclid / rem_euclid / abs_diff / midpoint, plus the
// float-shape predicates that are constant for a fixed-point type.
(@common $Type:ident, $Storage:ty) => {
impl<const SCALE: u32> $Type<SCALE> {
/// Euclidean division: the quotient as an integer multiple of
/// `ONE`, chosen so the remainder is non-negative. Panics on
/// `rhs == ZERO`.
#[inline]
#[must_use]
pub fn div_euclid(self, rhs: Self) -> Self {
Self(self.0.div_euclid(rhs.0) * Self::multiplier())
}
/// Euclidean remainder: `self - rhs * self.div_euclid(rhs)`,
/// always non-negative when `rhs != ZERO`. Both operands
/// share the scale, so no rescaling is needed. Panics on
/// `rhs == ZERO`.
#[inline]
#[must_use]
pub fn rem_euclid(self, rhs: Self) -> Self {
Self(self.0.rem_euclid(rhs.0))
}
/// Absolute difference `|self - rhs|`. Computed as
/// `max - min` so the subtraction is always non-negative.
#[inline]
#[must_use]
pub fn abs_diff(self, rhs: Self) -> Self {
Self(self.0.max(rhs.0) - self.0.min(rhs.0))
}
/// Midpoint of `self` and `rhs` without intermediate
/// overflow, rounding toward negative infinity. Uses the
/// branch-free `(a & b) + ((a ^ b) >> 1)` identity, which is
/// overflow-free and storage-agnostic.
#[inline]
#[must_use]
pub fn midpoint(self, rhs: Self) -> Self {
Self((self.0 & rhs.0) + ((self.0 ^ rhs.0) >> 1u32))
}
/// Always `false` — a fixed-point decimal has no NaN.
#[inline]
#[must_use]
pub const fn is_nan(self) -> bool {
false
}
/// Always `false` — a fixed-point decimal has no infinity.
#[inline]
#[must_use]
pub const fn is_infinite(self) -> bool {
false
}
/// Always `true` — every fixed-point decimal value is finite.
#[inline]
#[must_use]
pub const fn is_finite(self) -> bool {
true
}
/// `self * a + b`. Mirrors the `f64::mul_add` call shape so
/// f64-generic numeric code can monomorphise to a decimal
/// type; there is no hardware FMA — the multiply uses the
/// type's `Mul` and the add uses its `Add`.
#[inline]
#[must_use]
pub fn mul_add(self, a: Self, b: Self) -> Self {
self * a + b
}
}
};
}
pub(crate) use decl_decimal_int_methods;