Skip to main content

decimal_scaled/
lib.rs

1//! Const-generic base-10 fixed-point decimal types for deterministic arithmetic.
2//!
3//! # Overview
4//!
5//! `decimal-scaled` provides a family of fixed-point decimal types whose stored
6//! integer encodes `actual_value * 10^SCALE`. Decimal literals like `1.1`
7//! round-trip exactly without any binary approximation, and all core arithmetic
8//! is integer-only — identical bit-patterns on every platform.
9//!
10//! # Primary types
11//!
12//! Each width has a `D<digits><const SCALE: u32>` const-generic shape with the
13//! same method surface; pick the narrowest that fits your range.
14//!
15//! | Type | Storage | Max safe decimal digits | Feature gate |
16//! |------|---------|-------------------------|--------------|
17//! | [`D9<SCALE>`]  | `i32`    | 9   | always on |
18//! | [`D18<SCALE>`] | `i64`    | 18  | always on |
19//! | [`D38<SCALE>`] | `i128`   | 38  | always on |
20//! | [`D76<SCALE>`] | 256-bit  | 76  | `d76` or `wide` |
21//! | [`D153<SCALE>`] | 512-bit | 153 | `d153` or `wide` |
22//! | [`D307<SCALE>`] | 1024-bit | 307 | `d307` or `wide` |
23//!
24//! Concrete scale aliases such as `D38s12 = D38<12>` are emitted for every
25//! supported `SCALE`. `SCALE = MAX_SCALE + 1` is rejected at compile time —
26//! `10^(MAX_SCALE+1)` overflows the storage type.
27//!
28//! The width-generic [`Decimal`] trait carries the surface that is identical
29//! across widths (constants, arithmetic operators, sign methods, integer
30//! variants, pow / checked / wrapping / saturating / overflowing, float bridge,
31//! Euclidean / floor / ceil division, etc.). Use it to write helpers that work
32//! across widths; reach for the concrete type for width-specific operations
33//! like `rescale::<TARGET>()` whose const-generic parameter cannot live on a
34//! trait method.
35//!
36//! # Equality and hashing
37//!
38//! Because each logical value has exactly one representation at a fixed scale,
39//! `Hash`, `Eq`, `PartialEq`, `PartialOrd`, and `Ord` are all derived from the
40//! underlying integer storage. Two `Dxx<S>` values compare equal if and only
41//! if their raw bit patterns are identical. This gives predictable behaviour
42//! when decimal values are used as `HashMap` keys, unlike variable-scale
43//! decimal types where `1.10` and `1.1` may hash differently.
44//!
45//! # `num-traits` compatibility
46//!
47//! Every width implements the standard `num-traits` 0.2 surface:
48//! `Zero`, `One`, `Num`, `Bounded`, `Signed`, `FromPrimitive`,
49//! `ToPrimitive`, and the `Checked{Add,Sub,Mul,Div,Rem,Neg}` family
50//! (see [`::num_traits`]). These impls are unconditional (not behind a
51//! feature flag) because generic numeric code in the wider ecosystem
52//! consumes this surface by default.
53//!
54//! # `no_std` support
55//!
56//! The crate compiles with `no_std + alloc` when default features are
57//! disabled. `alloc` is required for `Display::to_string` and
58//! `FromStr::from_str`. Targets without `alloc` are not supported.
59//!
60//! # Feature flags
61//!
62//! - `std` (default): enables the fast implementations of transcendental
63//! functions (trigonometry, logarithms, exponentials, square root, cube
64//! root, float power) that delegate to platform `f64` intrinsics.
65//! - `alloc`: pulled in automatically; required for string formatting and
66//! parsing.
67//! - `serde`: enables `serde_helpers` for serialisation and deserialisation.
68//! - `strict`: enables integer-only implementations of all transcendental
69//! functions. When `strict` is active each function that would otherwise
70//! route through `f64` is instead implemented using integer-only
71//! algorithms. Explicit float-conversion methods (`to_f64`,
72//! `from_f64`, etc.) remain available regardless; they are type
73//! conversions, not mathematical operations. `strict` does not require
74//! `std`; the integer transcendental implementations compile under
75//! `no_std + alloc`.
76
77#![cfg_attr(not(feature = "std"), no_std)]
78#![cfg_attr(feature = "experimental-floats", feature(f16, f128))]
79// ── Clippy allow-list ─────────────────────────────────────────────────
80//
81// These are pedantic lints whose patterns this crate uses
82// intentionally and pervasively. Each is justified inline; allowing
83// them at the crate level is preferable to spraying per-site
84// `#[allow]` attributes or rewriting against the crate's domain.
85#![allow(
86    // Decimal width names overlap with type prefixes; the lint adds no
87    // signal here.
88    clippy::module_name_repetitions,
89    // We use unindented Markdown continuation in module docs.
90    clippy::doc_lazy_continuation,
91    // We routinely place a blank line between a method's `#[cfg]`
92    // attribute and its doc/body for readability.
93    clippy::empty_line_after_outer_attr,
94    // Big-integer arithmetic regularly casts between signed/unsigned
95    // and between widths. The wraps / truncations / sign flips are
96    // intentional — `unsigned_abs` paths, two's-complement tricks,
97    // narrowing the final result back to storage after a widened mul.
98    clippy::cast_possible_truncation,
99    clippy::cast_possible_wrap,
100    clippy::cast_sign_loss,
101    // We prefer `as` casts over `T::from(x)` in arithmetic-heavy
102    // inner loops for readability and to match the surrounding
103    // big-integer idiom.
104    clippy::cast_lossless,
105    // Float bridges (`to_f64`, `to_f32`) are explicitly lossy by
106    // contract. The lint is a tautology here.
107    clippy::cast_precision_loss,
108    // Literals like `1_000_000_000_000` carry the scale visually and
109    // are kept unseparated when they encode `10^SCALE`.
110    clippy::unreadable_literal,
111    // `if cond { panic!(…) }` is the crate's canonical bounds-check
112    // shape; `assert!(…)` would lose the dynamic message.
113    clippy::manual_assert,
114    // `Result<_, ()>` is the only honest error type for `const fn`
115    // digit-validity checks where no allocator is available.
116    clippy::result_unit_err,
117    // `if …; if …` chains read more cleanly than `if … && …` in the
118    // const-fn limb-arithmetic helpers.
119    clippy::collapsible_if,
120    // Big-int / fixed-point inner loops use `i`, `j`, `k`, `n`, `m`
121    // as conventional names. Renaming to `outer_index` etc. hurts
122    // readability without payoff.
123    clippy::similar_names,
124    clippy::many_single_char_names,
125    // Strict-transcendental kernels exceed 100 lines because they
126    // unroll a series-evaluation loop; splitting them just to please
127    // the line-count lint would scatter the algorithm.
128    clippy::too_many_lines,
129    // `#[inline(always)]` is set deliberately on small hot-path
130    // helpers (`apply_rounding`, `panic_or_wrap_*`). The lint
131    // assumes the inliner knows better; here we override on purpose.
132    clippy::inline_always,
133    // Strict-vs-fast comparisons in `tests/` deliberately compare
134    // raw `f64` results bit-for-bit. The lint can't tell test code
135    // from production.
136    clippy::float_cmp,
137    // Some narrow helpers `let result = …; result + 1` are flagged
138    // as let-else candidates; the explicit form is clearer in the
139    // big-int helpers.
140    clippy::manual_let_else,
141    // `format!("{x}") + "y"` is fine when both pieces stay tiny.
142    clippy::format_push_string,
143    // `if-else-if` chains over disjoint conditions sometimes read
144    // more clearly than `match` (especially with `<` / `>=` arms).
145    clippy::comparison_chain,
146    // Macro-emitted methods that return `Self` are wrapped with
147    // `#[must_use]` where it would catch bugs; the lint's
148    // recommendation on tiny constructors is noise.
149    clippy::must_use_candidate,
150    clippy::return_self_not_must_use,
151    // `# Errors` / `# Panics` sections: every public function's
152    // behaviour on error / panic is described in its main doc
153    // paragraph (and matches the pattern of the std-library
154    // primitive it shadows). The lint's per-section requirement
155    // adds boilerplate without information.
156    clippy::missing_errors_doc,
157    clippy::missing_panics_doc,
158    // Doc-comment backticks are added where they matter (type and
159    // function names); the lint flags every identifier-looking
160    // word, including math symbols and abbreviations.
161    clippy::doc_markdown,
162)]
163
164#[cfg(feature = "alloc")]
165extern crate alloc;
166
167mod arithmetic;
168#[cfg(feature = "bench-alt")]
169mod bench_alt;
170mod consts;
171mod consts_wide;
172mod core_type;
173mod decimal_trait;
174mod display;
175mod equalities;
176mod error;
177mod macros;
178mod num_traits;
179mod log_exp_strict;
180mod log_exp_fast;
181
182// `bitwise` and `num_traits_impls` used to live here as test-only
183// modules; their tests now run as Cargo integration tests under
184// `tests/`. The macro-generated impls themselves are emitted by
185// `decl_decimal_bitwise!` / `decl_decimal_num_traits_basics!` from
186// `core_type.rs`, alongside every other surface.
187mod rescale;
188mod rounding;
189mod mg_divide;
190mod d_w128_kernels;
191// `wide_int` is now unconditional. D38's strict transcendentals use
192// `Int512` as their guard-digit work integer (replacing the previous
193// `d_w128_kernels::Fixed` 256-bit sign-magnitude type), so the wide-
194// integer family must be available in every feature configuration —
195// not just `feature = "wide"` builds. Compile-time impact is modest:
196// ~2k LOC of self-contained limb arithmetic plus the per-width
197// `decl_wide_int!` instantiations.
198mod wide_int;
199mod overflow_variants;
200mod powers_strict;
201mod powers_fast;
202
203#[cfg(feature = "serde")]
204pub mod serde_helpers;
205// `trig` is compiled when it has any surface to emit: the integer-only
206// `*_strict` methods (present unless `fast`) or the f64-bridge
207// methods (present with `std`).
208#[cfg(any(not(feature = "fast"), feature = "std"))]
209mod trig_strict;
210mod trig_fast;
211
212
213pub use consts::DecimalConsts;
214pub use decimal_trait::Decimal;
215pub use error::{ConvertError, ParseError};
216pub use rounding::RoundingMode;
217
218// D38 — the 128-bit foundation, plus every scale alias D38s0..=D38s38.
219pub use core_type::{
220    D38, D38s0, D38s1, D38s2, D38s3, D38s4, D38s5, D38s6, D38s7, D38s8, D38s9, D38s10,
221    D38s11, D38s12, D38s13, D38s14, D38s15, D38s16, D38s17, D38s18, D38s19, D38s20,
222    D38s21, D38s22, D38s23, D38s24, D38s25, D38s26, D38s27, D38s28, D38s29, D38s30,
223    D38s31, D38s32, D38s33, D38s34, D38s35, D38s36, D38s37, D38s38,
224};
225
226// D9 — 32-bit storage, scale 0..=9.
227pub use core_type::{
228    D9, D9s0, D9s1, D9s2, D9s3, D9s4, D9s5, D9s6, D9s7, D9s8, D9s9,
229};
230
231// D18 — 64-bit storage, scale 0..=18.
232pub use core_type::{
233    D18, D18s0, D18s1, D18s2, D18s3, D18s4, D18s5, D18s6, D18s7, D18s8, D18s9, D18s10, D18s11,
234    D18s12, D18s13, D18s14, D18s15, D18s16, D18s17, D18s18,
235};
236
237// D76 — 256-bit storage, behind the `d76` / `wide` features.
238#[cfg(any(feature = "d76", feature = "wide"))]
239pub use core_type::{
240    D76, D76s0, D76s2, D76s6, D76s12, D76s18, D76s35, D76s50, D76s76,
241};
242
243// The hand-rolled wide-integer types — the storage backend for the
244// wide decimal tiers, also useful on their own.
245#[cfg(any(feature = "d76", feature = "d153", feature = "d307", feature = "wide"))]
246pub use wide_int::{
247    Int256, Int512, Int1024, Int2048, Int4096, Uint256, Uint512, Uint1024, Uint2048, Uint4096,
248};
249
250// D153 — 512-bit storage, behind the `d153` / `wide` features.
251#[cfg(any(feature = "d153", feature = "wide"))]
252pub use core_type::{D153, D153s0, D153s35, D153s75, D153s150, D153s153};
253
254// D307 — 1024-bit storage, behind the `d307` / `wide` features.
255#[cfg(any(feature = "d307", feature = "wide"))]
256pub use core_type::{D307, D307s0, D307s35, D307s150, D307s300, D307s307};
257
258// ─── Construction macros (re-exports + per-scale wrappers) ────────────
259
260/// The narrow-tier proc-macros are always available with the
261/// `macros` feature; the wide-tier proc-macros are additionally
262/// feature-gated to match their target type's availability.
263#[cfg(feature = "macros")]
264pub use decimal_scaled_macros::{d9, d18, d38};
265
266#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))]
267pub use decimal_scaled_macros::d76;
268
269#[cfg(all(feature = "macros", any(feature = "d153", feature = "wide")))]
270pub use decimal_scaled_macros::d153;
271
272#[cfg(all(feature = "macros", any(feature = "d307", feature = "wide", feature = "x-wide")))]
273pub use decimal_scaled_macros::d307;
274
275// Per-scale wrappers — curated subset of pre-baked
276// `<dN>s<SCALE>!` macros that forward to the corresponding
277// proc-macro with `scale N` added. Long-tail scales remain
278// reachable via the explicit `, scale N` qualifier.
279//
280// Each alias is a tiny `macro_rules!`. We don't generate them
281// through a nested macro because `macro_rules!` doesn't support
282// directly emitting another `macro_rules!` without `$$` escapes
283// that aren't available in stable Rust; explicit per-line
284// declarations keep things debuggable and only cost ~40 lines.
285
286// D9 curated scales.
287/// `d9s0!(value)` — equivalent to `d9!(value, scale 0)`.
288#[cfg(feature = "macros")] #[macro_export]
289macro_rules! d9s0  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d9!($v, scale 0  $(, $($rest)*)?) }; }
290/// `d9s2!(value)` — equivalent to `d9!(value, scale 2)`.
291#[cfg(feature = "macros")] #[macro_export]
292macro_rules! d9s2  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d9!($v, scale 2  $(, $($rest)*)?) }; }
293/// `d9s4!(value)` — equivalent to `d9!(value, scale 4)`.
294#[cfg(feature = "macros")] #[macro_export]
295macro_rules! d9s4  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d9!($v, scale 4  $(, $($rest)*)?) }; }
296/// `d9s6!(value)` — equivalent to `d9!(value, scale 6)`.
297#[cfg(feature = "macros")] #[macro_export]
298macro_rules! d9s6  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d9!($v, scale 6  $(, $($rest)*)?) }; }
299/// `d9s9!(value)` — equivalent to `d9!(value, scale 9)`.
300#[cfg(feature = "macros")] #[macro_export]
301macro_rules! d9s9  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d9!($v, scale 9  $(, $($rest)*)?) }; }
302
303// D18 curated scales.
304/// `d18s0!(value)` — equivalent to `d18!(value, scale 0)`.
305#[cfg(feature = "macros")] #[macro_export]
306macro_rules! d18s0  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d18!($v, scale 0  $(, $($rest)*)?) }; }
307/// `d18s2!(value)` — equivalent to `d18!(value, scale 2)`.
308#[cfg(feature = "macros")] #[macro_export]
309macro_rules! d18s2  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d18!($v, scale 2  $(, $($rest)*)?) }; }
310/// `d18s4!(value)` — equivalent to `d18!(value, scale 4)`.
311#[cfg(feature = "macros")] #[macro_export]
312macro_rules! d18s4  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d18!($v, scale 4  $(, $($rest)*)?) }; }
313/// `d18s6!(value)` — equivalent to `d18!(value, scale 6)`.
314#[cfg(feature = "macros")] #[macro_export]
315macro_rules! d18s6  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d18!($v, scale 6  $(, $($rest)*)?) }; }
316/// `d18s9!(value)` — equivalent to `d18!(value, scale 9)`.
317#[cfg(feature = "macros")] #[macro_export]
318macro_rules! d18s9  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d18!($v, scale 9  $(, $($rest)*)?) }; }
319/// `d18s12!(value)` — equivalent to `d18!(value, scale 12)`.
320#[cfg(feature = "macros")] #[macro_export]
321macro_rules! d18s12 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d18!($v, scale 12 $(, $($rest)*)?) }; }
322/// `d18s18!(value)` — equivalent to `d18!(value, scale 18)`.
323#[cfg(feature = "macros")] #[macro_export]
324macro_rules! d18s18 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d18!($v, scale 18 $(, $($rest)*)?) }; }
325
326// D38 curated scales.
327/// `d38s0!(value)` — equivalent to `d38!(value, scale 0)`.
328#[cfg(feature = "macros")] #[macro_export]
329macro_rules! d38s0  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 0  $(, $($rest)*)?) }; }
330/// `d38s2!(value)` — equivalent to `d38!(value, scale 2)`.
331#[cfg(feature = "macros")] #[macro_export]
332macro_rules! d38s2  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 2  $(, $($rest)*)?) }; }
333/// `d38s4!(value)` — equivalent to `d38!(value, scale 4)`.
334#[cfg(feature = "macros")] #[macro_export]
335macro_rules! d38s4  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 4  $(, $($rest)*)?) }; }
336/// `d38s6!(value)` — equivalent to `d38!(value, scale 6)`.
337#[cfg(feature = "macros")] #[macro_export]
338macro_rules! d38s6  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 6  $(, $($rest)*)?) }; }
339/// `d38s8!(value)` — equivalent to `d38!(value, scale 8)`.
340#[cfg(feature = "macros")] #[macro_export]
341macro_rules! d38s8  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 8  $(, $($rest)*)?) }; }
342/// `d38s9!(value)` — equivalent to `d38!(value, scale 9)`.
343#[cfg(feature = "macros")] #[macro_export]
344macro_rules! d38s9  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 9  $(, $($rest)*)?) }; }
345/// `d38s12!(value)` — equivalent to `d38!(value, scale 12)`.
346#[cfg(feature = "macros")] #[macro_export]
347macro_rules! d38s12 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 12 $(, $($rest)*)?) }; }
348/// `d38s15!(value)` — equivalent to `d38!(value, scale 15)`.
349#[cfg(feature = "macros")] #[macro_export]
350macro_rules! d38s15 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 15 $(, $($rest)*)?) }; }
351/// `d38s18!(value)` — equivalent to `d38!(value, scale 18)`.
352#[cfg(feature = "macros")] #[macro_export]
353macro_rules! d38s18 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 18 $(, $($rest)*)?) }; }
354/// `d38s24!(value)` — equivalent to `d38!(value, scale 24)`.
355#[cfg(feature = "macros")] #[macro_export]
356macro_rules! d38s24 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 24 $(, $($rest)*)?) }; }
357/// `d38s35!(value)` — equivalent to `d38!(value, scale 35)`.
358#[cfg(feature = "macros")] #[macro_export]
359macro_rules! d38s35 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 35 $(, $($rest)*)?) }; }
360/// `d38s38!(value)` — equivalent to `d38!(value, scale 38)`.
361#[cfg(feature = "macros")] #[macro_export]
362macro_rules! d38s38 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 38 $(, $($rest)*)?) }; }
363
364// D76 curated scales.
365/// `d76s0!(value)` — equivalent to `d76!(value, scale 0)`.
366#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))] #[macro_export]
367macro_rules! d76s0  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d76!($v, scale 0  $(, $($rest)*)?) }; }
368/// `d76s2!(value)` — equivalent to `d76!(value, scale 2)`.
369#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))] #[macro_export]
370macro_rules! d76s2  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d76!($v, scale 2  $(, $($rest)*)?) }; }
371/// `d76s6!(value)` — equivalent to `d76!(value, scale 6)`.
372#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))] #[macro_export]
373macro_rules! d76s6  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d76!($v, scale 6  $(, $($rest)*)?) }; }
374/// `d76s12!(value)` — equivalent to `d76!(value, scale 12)`.
375#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))] #[macro_export]
376macro_rules! d76s12 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d76!($v, scale 12 $(, $($rest)*)?) }; }
377/// `d76s18!(value)` — equivalent to `d76!(value, scale 18)`.
378#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))] #[macro_export]
379macro_rules! d76s18 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d76!($v, scale 18 $(, $($rest)*)?) }; }
380/// `d76s35!(value)` — equivalent to `d76!(value, scale 35)`.
381#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))] #[macro_export]
382macro_rules! d76s35 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d76!($v, scale 35 $(, $($rest)*)?) }; }
383/// `d76s50!(value)` — equivalent to `d76!(value, scale 50)`.
384#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))] #[macro_export]
385macro_rules! d76s50 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d76!($v, scale 50 $(, $($rest)*)?) }; }
386/// `d76s76!(value)` — equivalent to `d76!(value, scale 76)`.
387#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))] #[macro_export]
388macro_rules! d76s76 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d76!($v, scale 76 $(, $($rest)*)?) }; }
389
390// D153 curated scales.
391/// `d153s0!(value)` — equivalent to `d153!(value, scale 0)`.
392#[cfg(all(feature = "macros", any(feature = "d153", feature = "wide")))] #[macro_export]
393macro_rules! d153s0   { ($v:tt $(, $($rest:tt)*)?) => { $crate::d153!($v, scale 0   $(, $($rest)*)?) }; }
394/// `d153s35!(value)` — equivalent to `d153!(value, scale 35)`.
395#[cfg(all(feature = "macros", any(feature = "d153", feature = "wide")))] #[macro_export]
396macro_rules! d153s35  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d153!($v, scale 35  $(, $($rest)*)?) }; }
397/// `d153s75!(value)` — equivalent to `d153!(value, scale 75)`.
398#[cfg(all(feature = "macros", any(feature = "d153", feature = "wide")))] #[macro_export]
399macro_rules! d153s75  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d153!($v, scale 75  $(, $($rest)*)?) }; }
400/// `d153s150!(value)` — equivalent to `d153!(value, scale 150)`.
401#[cfg(all(feature = "macros", any(feature = "d153", feature = "wide")))] #[macro_export]
402macro_rules! d153s150 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d153!($v, scale 150 $(, $($rest)*)?) }; }
403/// `d153s153!(value)` — equivalent to `d153!(value, scale 153)`.
404#[cfg(all(feature = "macros", any(feature = "d153", feature = "wide")))] #[macro_export]
405macro_rules! d153s153 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d153!($v, scale 153 $(, $($rest)*)?) }; }
406
407// D307 curated scales.
408/// `d307s0!(value)` — equivalent to `d307!(value, scale 0)`.
409#[cfg(all(feature = "macros", any(feature = "d307", feature = "wide", feature = "x-wide")))] #[macro_export]
410macro_rules! d307s0   { ($v:tt $(, $($rest:tt)*)?) => { $crate::d307!($v, scale 0   $(, $($rest)*)?) }; }
411/// `d307s35!(value)` — equivalent to `d307!(value, scale 35)`.
412#[cfg(all(feature = "macros", any(feature = "d307", feature = "wide", feature = "x-wide")))] #[macro_export]
413macro_rules! d307s35  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d307!($v, scale 35  $(, $($rest)*)?) }; }
414/// `d307s150!(value)` — equivalent to `d307!(value, scale 150)`.
415#[cfg(all(feature = "macros", any(feature = "d307", feature = "wide", feature = "x-wide")))] #[macro_export]
416macro_rules! d307s150 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d307!($v, scale 150 $(, $($rest)*)?) }; }
417/// `d307s300!(value)` — equivalent to `d307!(value, scale 300)`.
418#[cfg(all(feature = "macros", any(feature = "d307", feature = "wide", feature = "x-wide")))] #[macro_export]
419macro_rules! d307s300 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d307!($v, scale 300 $(, $($rest)*)?) }; }
420/// `d307s307!(value)` — equivalent to `d307!(value, scale 307)`.
421#[cfg(all(feature = "macros", any(feature = "d307", feature = "wide", feature = "x-wide")))] #[macro_export]
422macro_rules! d307s307 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d307!($v, scale 307 $(, $($rest)*)?) }; }