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. The number on
14//! every `D{N}` type is `MAX_SCALE` — the highest `SCALE` the storage can hold.
15//!
16//! | Type | Storage | `MAX_SCALE` | Feature gate |
17//! |------|---------|-------------|--------------|
18//! | [`D9<SCALE>`]    | `i32`     |    9 | always on |
19//! | [`D18<SCALE>`]   | `i64`     |   18 | always on |
20//! | [`D38<SCALE>`]   | `i128`    |   38 | always on |
21//! | [`D56<SCALE>`]   | 192-bit   |   57 | `d56` or `wide` |
22//! | [`D76<SCALE>`]   | 256-bit   |   76 | `d76` or `wide` |
23//! | [`D114<SCALE>`]  | 384-bit   |  115 | `d114` or `wide` |
24//! | [`D153<SCALE>`]  | 512-bit   |  153 | `d153` or `wide` |
25//! | [`D230<SCALE>`]  | 768-bit   |  230 | `d230` or `wide` |
26//! | [`D307<SCALE>`]  | 1024-bit  |  307 | `d307` or `wide` |
27//! | [`D461<SCALE>`]  | 1536-bit  |  462 | `d461` or `x-wide` |
28//! | [`D615<SCALE>`]  | 2048-bit  |  616 | `d615` or `x-wide` |
29//! | [`D923<SCALE>`]  | 3072-bit  |  924 | `d923` or `xx-wide` |
30//! | [`D1231<SCALE>`] | 4096-bit  | 1232 | `d1231` or `xx-wide` |
31//!
32//! Umbrellas: `wide` enables D56 / D76 / D114 / D153 / D230 / D307;
33//! `x-wide` adds D461 + D615; `xx-wide` adds D923 + D1231. Every
34//! adjacent pair has lossless `.widen()` / fallible `.narrow()`
35//! helpers plus `From` / `TryFrom` impls.
36//!
37//! Concrete scale aliases such as `D38s12 = D38<12>` are emitted for every
38//! supported `SCALE`. `SCALE = MAX_SCALE + 1` is rejected at compile time —
39//! `10^(MAX_SCALE+1)` overflows the storage type.
40//!
41//! The width-generic [`Decimal`] trait carries the surface that is identical
42//! across widths (constants, arithmetic operators, sign methods, integer
43//! variants, pow / checked / wrapping / saturating / overflowing, float bridge,
44//! Euclidean / floor / ceil division, etc.). Use it to write helpers that work
45//! across widths; reach for the concrete type for width-specific operations
46//! like `rescale::<TARGET>()` whose const-generic parameter cannot live on a
47//! trait method.
48//!
49//! # Equality and hashing
50//!
51//! Because each logical value has exactly one representation at a fixed scale,
52//! `Hash`, `Eq`, `PartialEq`, `PartialOrd`, and `Ord` are all derived from the
53//! underlying integer storage. Two `Dxx<S>` values compare equal if and only
54//! if their raw bit patterns are identical. This gives predictable behaviour
55//! when decimal values are used as `HashMap` keys, unlike variable-scale
56//! decimal types where `1.10` and `1.1` may hash differently.
57//!
58//! # `num-traits` compatibility
59//!
60//! Every width implements the standard `num-traits` 0.2 surface:
61//! `Zero`, `One`, `Num`, `Bounded`, `Signed`, `FromPrimitive`,
62//! `ToPrimitive`, and the `Checked{Add,Sub,Mul,Div,Rem,Neg}` family
63//! (see [`::num_traits`]). These impls are unconditional (not behind a
64//! feature flag) because generic numeric code in the wider ecosystem
65//! consumes this surface by default.
66//!
67//! # `no_std` support
68//!
69//! The crate compiles with `no_std + alloc` when default features are
70//! disabled. `alloc` is required for `Display::to_string` and
71//! `FromStr::from_str`. Targets without `alloc` are not supported.
72//!
73//! # Feature flags
74//!
75//! - `std` (default): enables the fast implementations of transcendental
76//! functions (trigonometry, logarithms, exponentials, square root, cube
77//! root, float power) that delegate to platform `f64` intrinsics.
78//! - `alloc`: pulled in automatically; required for string formatting and
79//! parsing.
80//! - `serde`: enables `serde_helpers` for serialisation and deserialisation.
81//! - `strict`: enables integer-only implementations of all transcendental
82//! functions. When `strict` is active each function that would otherwise
83//! route through `f64` is instead implemented using integer-only
84//! algorithms. Explicit float-conversion methods (`to_f64`,
85//! `from_f64`, etc.) remain available regardless; they are type
86//! conversions, not mathematical operations. `strict` does not require
87//! `std`; the integer transcendental implementations compile under
88//! `no_std + alloc`.
89
90#![cfg_attr(not(feature = "std"), no_std)]
91#![cfg_attr(feature = "experimental-floats", feature(f16, f128))]
92// ── Clippy allow-list ─────────────────────────────────────────────────
93//
94// These are pedantic lints whose patterns this crate uses
95// intentionally and pervasively. Each is justified inline; allowing
96// them at the crate level is preferable to spraying per-site
97// `#[allow]` attributes or rewriting against the crate's domain.
98#![allow(
99    // Decimal width names overlap with type prefixes; the lint adds no
100    // signal here.
101    clippy::module_name_repetitions,
102    // We use unindented Markdown continuation in module docs.
103    clippy::doc_lazy_continuation,
104    // We routinely place a blank line between a method's `#[cfg]`
105    // attribute and its doc/body for readability.
106    clippy::empty_line_after_outer_attr,
107    // Big-integer arithmetic regularly casts between signed/unsigned
108    // and between widths. The wraps / truncations / sign flips are
109    // intentional — `unsigned_abs` paths, two's-complement tricks,
110    // narrowing the final result back to storage after a widened mul.
111    clippy::cast_possible_truncation,
112    clippy::cast_possible_wrap,
113    clippy::cast_sign_loss,
114    // We prefer `as` casts over `T::from(x)` in arithmetic-heavy
115    // inner loops for readability and to match the surrounding
116    // big-integer idiom.
117    clippy::cast_lossless,
118    // Float bridges (`to_f64`, `to_f32`) are explicitly lossy by
119    // contract. The lint is a tautology here.
120    clippy::cast_precision_loss,
121    // Literals like `1_000_000_000_000` carry the scale visually and
122    // are kept unseparated when they encode `10^SCALE`.
123    clippy::unreadable_literal,
124    // `if cond { panic!(…) }` is the crate's canonical bounds-check
125    // shape; `assert!(…)` would lose the dynamic message.
126    clippy::manual_assert,
127    // `Result<_, ()>` is the only honest error type for `const fn`
128    // digit-validity checks where no allocator is available.
129    clippy::result_unit_err,
130    // `if …; if …` chains read more cleanly than `if … && …` in the
131    // const-fn limb-arithmetic helpers.
132    clippy::collapsible_if,
133    // Big-int / fixed-point inner loops use `i`, `j`, `k`, `n`, `m`
134    // as conventional names. Renaming to `outer_index` etc. hurts
135    // readability without payoff.
136    clippy::similar_names,
137    clippy::many_single_char_names,
138    // Strict-transcendental kernels exceed 100 lines because they
139    // unroll a series-evaluation loop; splitting them just to please
140    // the line-count lint would scatter the algorithm.
141    clippy::too_many_lines,
142    // `#[inline(always)]` is set deliberately on small hot-path
143    // helpers (`apply_rounding`, `panic_or_wrap_*`). The lint
144    // assumes the inliner knows better; here we override on purpose.
145    clippy::inline_always,
146    // Strict-vs-fast comparisons in `tests/` deliberately compare
147    // raw `f64` results bit-for-bit. The lint can't tell test code
148    // from production.
149    clippy::float_cmp,
150    // Some narrow helpers `let result = …; result + 1` are flagged
151    // as let-else candidates; the explicit form is clearer in the
152    // big-int helpers.
153    clippy::manual_let_else,
154    // `format!("{x}") + "y"` is fine when both pieces stay tiny.
155    clippy::format_push_string,
156    // `if-else-if` chains over disjoint conditions sometimes read
157    // more clearly than `match` (especially with `<` / `>=` arms).
158    clippy::comparison_chain,
159    // Macro-emitted methods that return `Self` are wrapped with
160    // `#[must_use]` where it would catch bugs; the lint's
161    // recommendation on tiny constructors is noise.
162    clippy::must_use_candidate,
163    clippy::return_self_not_must_use,
164    // `# Errors` / `# Panics` sections: every public function's
165    // behaviour on error / panic is described in its main doc
166    // paragraph (and matches the pattern of the std-library
167    // primitive it shadows). The lint's per-section requirement
168    // adds boilerplate without information.
169    clippy::missing_errors_doc,
170    clippy::missing_panics_doc,
171    // Doc-comment backticks are added where they matter (type and
172    // function names); the lint flags every identifier-looking
173    // word, including math symbols and abbreviations.
174    clippy::doc_markdown,
175)]
176
177#[cfg(feature = "alloc")]
178extern crate alloc;
179
180// Re-export `tracing` under the crate so the perf-trace cfg-gated
181// `info_span!` calls in macro-emitted modules can reach it via
182// `$crate::tracing::…`. Internal-only — gated by the same feature.
183#[cfg(feature = "perf-trace")]
184#[doc(hidden)]
185pub use ::tracing;
186
187mod arithmetic;
188#[cfg(feature = "bench-alt")]
189mod bench_alt;
190#[cfg(feature = "bench-alt")]
191#[doc(hidden)]
192pub mod __bench_internals {
193    #[inline(never)]
194    pub fn limbs_mul(a: &[u128], b: &[u128], out: &mut [u128]) {
195        crate::wide_int::limbs_mul(a, b, out)
196    }
197    #[inline(never)]
198    pub fn limbs_mul_fast(a: &[u128], b: &[u128], out: &mut [u128]) {
199        crate::wide_int::limbs_mul_fast(a, b, out)
200    }
201    #[inline(never)]
202    pub fn mul_slice(a: &[u64], b: &[u64], out: &mut [u64]) {
203        crate::wide_int::limbs_mul_u64(a, b, out)
204    }
205    #[inline(never)]
206    pub fn mul_fixed<const L: usize, const D: usize>(
207        a: &[u64; L],
208        b: &[u64; L],
209        out: &mut [u64; D],
210    ) {
211        crate::wide_int::limbs_mul_u64_fixed::<L, D>(a, b, out)
212    }
213}
214mod consts;
215mod consts_wide;
216mod core_type;
217mod decimal_trait;
218mod display;
219mod equalities;
220mod error;
221mod macros;
222mod num_traits;
223mod log_exp_strict;
224mod log_exp_fast;
225
226// `bitwise` and `num_traits_impls` used to live here as test-only
227// modules; their tests now run as Cargo integration tests under
228// `tests/`. The macro-generated impls themselves are emitted by
229// `decl_decimal_bitwise!` / `decl_decimal_num_traits_basics!` from
230// `core_type.rs`, alongside every other surface.
231mod rescale;
232mod rounding;
233mod mg_divide;
234mod d_w128_kernels;
235// `wide_int` is now unconditional. D38's strict transcendentals use
236// `Int512` as their guard-digit work integer (replacing the previous
237// `d_w128_kernels::Fixed` 256-bit sign-magnitude type), so the wide-
238// integer family must be available in every feature configuration —
239// not just `feature = "wide"` builds. Compile-time impact is modest:
240// ~2k LOC of self-contained limb arithmetic plus the per-width
241// `decl_wide_int!` instantiations.
242mod wide_int;
243mod overflow_variants;
244mod powers_strict;
245mod powers_fast;
246
247#[cfg(feature = "serde")]
248pub mod serde_helpers;
249// `trig` is compiled when it has any surface to emit: the integer-only
250// `*_strict` methods (present unless `fast`) or the f64-bridge
251// methods (present with `std`).
252#[cfg(any(not(feature = "fast"), feature = "std"))]
253mod trig_strict;
254mod trig_fast;
255
256
257pub use consts::DecimalConsts;
258pub use decimal_trait::Decimal;
259pub use error::{ConvertError, ParseError};
260pub use rounding::RoundingMode;
261
262// D38 — the 128-bit foundation, plus every scale alias D38s0..=D38s38.
263pub use core_type::{
264    D38, D38s0, D38s1, D38s2, D38s3, D38s4, D38s5, D38s6, D38s7, D38s8, D38s9, D38s10,
265    D38s11, D38s12, D38s13, D38s14, D38s15, D38s16, D38s17, D38s18, D38s19, D38s20,
266    D38s21, D38s22, D38s23, D38s24, D38s25, D38s26, D38s27, D38s28, D38s29, D38s30,
267    D38s31, D38s32, D38s33, D38s34, D38s35, D38s36, D38s37, D38s38,
268};
269
270// D9 — 32-bit storage, scale 0..=9.
271pub use core_type::{
272    D9, D9s0, D9s1, D9s2, D9s3, D9s4, D9s5, D9s6, D9s7, D9s8, D9s9,
273};
274
275// D18 — 64-bit storage, scale 0..=18.
276pub use core_type::{
277    D18, D18s0, D18s1, D18s2, D18s3, D18s4, D18s5, D18s6, D18s7, D18s8, D18s9, D18s10, D18s11,
278    D18s12, D18s13, D18s14, D18s15, D18s16, D18s17, D18s18,
279};
280
281// D76 — 256-bit storage, behind the `d76` / `wide` features.
282#[cfg(any(feature = "d76", feature = "wide"))]
283pub use core_type::{
284    D76,
285    D76s0, D76s1, D76s2, D76s3, D76s4, D76s6, D76s9, D76s12, D76s15,
286    D76s18, D76s20, D76s24, D76s28, D76s32, D76s35, D76s38, D76s42,
287    D76s48, D76s50, D76s56, D76s64, D76s70, D76s75, D76s76,
288};
289
290// The hand-rolled wide-integer types — the storage backend for the
291// wide decimal tiers, also useful on their own.
292#[cfg(any(feature = "d76", feature = "d153", feature = "d307", feature = "wide"))]
293pub use wide_int::{
294    Int256, Int512, Int1024, Int2048, Int4096, Uint256, Uint512, Uint1024, Uint2048, Uint4096,
295};
296
297// D153 — 512-bit storage, behind the `d153` / `wide` features.
298#[cfg(any(feature = "d153", feature = "wide"))]
299pub use core_type::{
300    D153,
301    D153s0, D153s1, D153s2, D153s4, D153s6, D153s9, D153s12, D153s15,
302    D153s18, D153s20, D153s24, D153s28, D153s32, D153s35, D153s38,
303    D153s50, D153s57, D153s75, D153s76, D153s100, D153s115, D153s140,
304    D153s150, D153s152, D153s153,
305};
306
307// D307 — 1024-bit storage, behind the `d307` / `wide` features.
308#[cfg(any(feature = "d307", feature = "wide"))]
309pub use core_type::{
310    D307,
311    D307s0, D307s1, D307s2, D307s4, D307s6, D307s9, D307s12, D307s15,
312    D307s18, D307s20, D307s24, D307s28, D307s32, D307s35, D307s38,
313    D307s50, D307s75, D307s100, D307s115, D307s150, D307s153,
314    D307s200, D307s230, D307s275, D307s300, D307s306, D307s307,
315};
316
317// ─── New half-width and wider tiers ───────────────────────────────────
318
319// D56 — 192-bit storage; half-width between D38 and D76.
320#[cfg(any(feature = "d56", feature = "wide"))]
321pub use core_type::{
322    D56,
323    D56s0, D56s1, D56s2, D56s4, D56s6, D56s9, D56s12, D56s18, D56s20, D56s24,
324    D56s28, D56s32, D56s38, D56s42, D56s48, D56s52, D56s56, D56s57,
325};
326#[cfg(any(feature = "d56", feature = "wide"))]
327pub use wide_int::{Int192, Uint192};
328
329// D114 — 384-bit; half-width between D76 and D153.
330#[cfg(any(feature = "d114", feature = "wide"))]
331pub use core_type::{
332    D114,
333    D114s0, D114s1, D114s4, D114s8, D114s16, D114s24, D114s32, D114s38, D114s50,
334    D114s57, D114s64, D114s76, D114s90, D114s100, D114s110, D114s114, D114s115,
335};
336#[cfg(any(feature = "d114", feature = "wide"))]
337pub use wide_int::{Int384, Uint384};
338
339// D230 — 768-bit; half-width between D153 and D307.
340#[cfg(any(feature = "d230", feature = "wide"))]
341pub use core_type::{
342    D230,
343    D230s0, D230s1, D230s6, D230s18, D230s38, D230s57, D230s75, D230s100, D230s115,
344    D230s140, D230s153, D230s175, D230s200, D230s215, D230s225, D230s229, D230s230,
345};
346#[cfg(any(feature = "d230", feature = "wide"))]
347pub use wide_int::{Int768, Uint768};
348
349// D461 — 1536-bit; half-width between D307 and D615.
350#[cfg(any(feature = "d461", feature = "x-wide"))]
351pub use core_type::{
352    D461,
353    D461s0, D461s1, D461s18, D461s38, D461s75, D461s115, D461s153, D461s200, D461s230,
354    D461s275, D461s307, D461s350, D461s400, D461s440, D461s460, D461s461, D461s462,
355};
356#[cfg(any(feature = "d461", feature = "x-wide"))]
357pub use wide_int::{Int1536, Uint1536};
358
359// D615 — 2048-bit; new top wide tier. Int2048 / Uint2048 are
360// already exported above for x-wide / d307 widening; no re-export
361// here.
362#[cfg(any(feature = "d615", feature = "x-wide"))]
363pub use core_type::{
364    D615,
365    D615s0, D615s1, D615s38, D615s75, D615s115, D615s153, D615s200, D615s230, D615s275,
366    D615s308, D615s380, D615s462, D615s500, D615s555, D615s600, D615s615, D615s616,
367};
368
369// D923 — 3072-bit; half-width between D615 and D1231.
370#[cfg(any(feature = "d923", feature = "xx-wide"))]
371pub use core_type::{
372    D923,
373    D923s0, D923s1, D923s75, D923s153, D923s230, D923s307, D923s400, D923s461, D923s462,
374    D923s500, D923s616, D923s700, D923s800, D923s860, D923s900, D923s920, D923s923, D923s924,
375};
376#[cfg(any(feature = "d923", feature = "xx-wide"))]
377pub use wide_int::{Int3072, Int6144, Int12288, Uint3072, Uint6144, Uint12288};
378
379// D1231 — 4096-bit; widest tier shipped.
380#[cfg(any(feature = "d1231", feature = "xx-wide"))]
381pub use core_type::{
382    D1231,
383    D1231s0, D1231s1, D1231s75, D1231s153, D1231s230, D1231s307, D1231s461, D1231s616,
384    D1231s700, D1231s800, D1231s900, D1231s924, D1231s1000, D1231s1100,
385    D1231s1180, D1231s1220, D1231s1230, D1231s1231, D1231s1232,
386};
387#[cfg(any(feature = "d1231", feature = "xx-wide"))]
388pub use wide_int::{Int8192, Int16384, Uint8192, Uint16384};
389
390// ─── Construction macros (re-exports + per-scale wrappers) ────────────
391
392/// The narrow-tier proc-macros are always available with the
393/// `macros` feature; the wide-tier proc-macros are additionally
394/// feature-gated to match their target type's availability.
395#[cfg(feature = "macros")]
396pub use decimal_scaled_macros::{d9, d18, d38};
397
398#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))]
399pub use decimal_scaled_macros::d76;
400
401#[cfg(all(feature = "macros", any(feature = "d153", feature = "wide")))]
402pub use decimal_scaled_macros::d153;
403
404#[cfg(all(feature = "macros", any(feature = "d307", feature = "wide", feature = "x-wide")))]
405pub use decimal_scaled_macros::d307;
406
407#[cfg(all(feature = "macros", any(feature = "d56", feature = "wide")))]
408pub use decimal_scaled_macros::d56;
409
410#[cfg(all(feature = "macros", any(feature = "d114", feature = "wide")))]
411pub use decimal_scaled_macros::d114;
412
413#[cfg(all(feature = "macros", any(feature = "d230", feature = "wide")))]
414pub use decimal_scaled_macros::d230;
415
416#[cfg(all(feature = "macros", any(feature = "d461", feature = "x-wide")))]
417pub use decimal_scaled_macros::d461;
418
419#[cfg(all(feature = "macros", any(feature = "d615", feature = "x-wide")))]
420pub use decimal_scaled_macros::d615;
421
422#[cfg(all(feature = "macros", any(feature = "d923", feature = "xx-wide")))]
423pub use decimal_scaled_macros::d923;
424
425#[cfg(all(feature = "macros", any(feature = "d1231", feature = "xx-wide")))]
426pub use decimal_scaled_macros::d1231;
427
428// Per-scale wrappers — curated subset of pre-baked
429// `<dN>s<SCALE>!` macros that forward to the corresponding
430// proc-macro with `scale N` added. Long-tail scales remain
431// reachable via the explicit `, scale N` qualifier.
432//
433// Each alias is a tiny `macro_rules!`. We don't generate them
434// through a nested macro because `macro_rules!` doesn't support
435// directly emitting another `macro_rules!` without `$$` escapes
436// that aren't available in stable Rust; explicit per-line
437// declarations keep things debuggable and only cost ~40 lines.
438
439// D9 curated scales.
440/// `d9s0!(value)` — equivalent to `d9!(value, scale 0)`.
441#[cfg(feature = "macros")] #[macro_export]
442macro_rules! d9s0  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d9!($v, scale 0  $(, $($rest)*)?) }; }
443/// `d9s2!(value)` — equivalent to `d9!(value, scale 2)`.
444#[cfg(feature = "macros")] #[macro_export]
445macro_rules! d9s2  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d9!($v, scale 2  $(, $($rest)*)?) }; }
446/// `d9s4!(value)` — equivalent to `d9!(value, scale 4)`.
447#[cfg(feature = "macros")] #[macro_export]
448macro_rules! d9s4  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d9!($v, scale 4  $(, $($rest)*)?) }; }
449/// `d9s6!(value)` — equivalent to `d9!(value, scale 6)`.
450#[cfg(feature = "macros")] #[macro_export]
451macro_rules! d9s6  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d9!($v, scale 6  $(, $($rest)*)?) }; }
452/// `d9s9!(value)` — equivalent to `d9!(value, scale 9)`.
453#[cfg(feature = "macros")] #[macro_export]
454macro_rules! d9s9  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d9!($v, scale 9  $(, $($rest)*)?) }; }
455
456// D18 curated scales.
457/// `d18s0!(value)` — equivalent to `d18!(value, scale 0)`.
458#[cfg(feature = "macros")] #[macro_export]
459macro_rules! d18s0  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d18!($v, scale 0  $(, $($rest)*)?) }; }
460/// `d18s2!(value)` — equivalent to `d18!(value, scale 2)`.
461#[cfg(feature = "macros")] #[macro_export]
462macro_rules! d18s2  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d18!($v, scale 2  $(, $($rest)*)?) }; }
463/// `d18s4!(value)` — equivalent to `d18!(value, scale 4)`.
464#[cfg(feature = "macros")] #[macro_export]
465macro_rules! d18s4  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d18!($v, scale 4  $(, $($rest)*)?) }; }
466/// `d18s6!(value)` — equivalent to `d18!(value, scale 6)`.
467#[cfg(feature = "macros")] #[macro_export]
468macro_rules! d18s6  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d18!($v, scale 6  $(, $($rest)*)?) }; }
469/// `d18s9!(value)` — equivalent to `d18!(value, scale 9)`.
470#[cfg(feature = "macros")] #[macro_export]
471macro_rules! d18s9  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d18!($v, scale 9  $(, $($rest)*)?) }; }
472/// `d18s12!(value)` — equivalent to `d18!(value, scale 12)`.
473#[cfg(feature = "macros")] #[macro_export]
474macro_rules! d18s12 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d18!($v, scale 12 $(, $($rest)*)?) }; }
475/// `d18s18!(value)` — equivalent to `d18!(value, scale 18)`.
476#[cfg(feature = "macros")] #[macro_export]
477macro_rules! d18s18 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d18!($v, scale 18 $(, $($rest)*)?) }; }
478
479// D38 curated scales.
480/// `d38s0!(value)` — equivalent to `d38!(value, scale 0)`.
481#[cfg(feature = "macros")] #[macro_export]
482macro_rules! d38s0  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 0  $(, $($rest)*)?) }; }
483/// `d38s2!(value)` — equivalent to `d38!(value, scale 2)`.
484#[cfg(feature = "macros")] #[macro_export]
485macro_rules! d38s2  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 2  $(, $($rest)*)?) }; }
486/// `d38s4!(value)` — equivalent to `d38!(value, scale 4)`.
487#[cfg(feature = "macros")] #[macro_export]
488macro_rules! d38s4  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 4  $(, $($rest)*)?) }; }
489/// `d38s6!(value)` — equivalent to `d38!(value, scale 6)`.
490#[cfg(feature = "macros")] #[macro_export]
491macro_rules! d38s6  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 6  $(, $($rest)*)?) }; }
492/// `d38s8!(value)` — equivalent to `d38!(value, scale 8)`.
493#[cfg(feature = "macros")] #[macro_export]
494macro_rules! d38s8  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 8  $(, $($rest)*)?) }; }
495/// `d38s9!(value)` — equivalent to `d38!(value, scale 9)`.
496#[cfg(feature = "macros")] #[macro_export]
497macro_rules! d38s9  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 9  $(, $($rest)*)?) }; }
498/// `d38s12!(value)` — equivalent to `d38!(value, scale 12)`.
499#[cfg(feature = "macros")] #[macro_export]
500macro_rules! d38s12 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 12 $(, $($rest)*)?) }; }
501/// `d38s15!(value)` — equivalent to `d38!(value, scale 15)`.
502#[cfg(feature = "macros")] #[macro_export]
503macro_rules! d38s15 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 15 $(, $($rest)*)?) }; }
504/// `d38s18!(value)` — equivalent to `d38!(value, scale 18)`.
505#[cfg(feature = "macros")] #[macro_export]
506macro_rules! d38s18 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 18 $(, $($rest)*)?) }; }
507/// `d38s24!(value)` — equivalent to `d38!(value, scale 24)`.
508#[cfg(feature = "macros")] #[macro_export]
509macro_rules! d38s24 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 24 $(, $($rest)*)?) }; }
510/// `d38s35!(value)` — equivalent to `d38!(value, scale 35)`.
511#[cfg(feature = "macros")] #[macro_export]
512macro_rules! d38s35 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 35 $(, $($rest)*)?) }; }
513/// `d38s38!(value)` — equivalent to `d38!(value, scale 38)`.
514#[cfg(feature = "macros")] #[macro_export]
515macro_rules! d38s38 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 38 $(, $($rest)*)?) }; }
516
517// D76 curated scales.
518/// `d76s0!(value)` — equivalent to `d76!(value, scale 0)`.
519#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))] #[macro_export]
520macro_rules! d76s0  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d76!($v, scale 0  $(, $($rest)*)?) }; }
521/// `d76s2!(value)` — equivalent to `d76!(value, scale 2)`.
522#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))] #[macro_export]
523macro_rules! d76s2  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d76!($v, scale 2  $(, $($rest)*)?) }; }
524/// `d76s6!(value)` — equivalent to `d76!(value, scale 6)`.
525#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))] #[macro_export]
526macro_rules! d76s6  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d76!($v, scale 6  $(, $($rest)*)?) }; }
527/// `d76s12!(value)` — equivalent to `d76!(value, scale 12)`.
528#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))] #[macro_export]
529macro_rules! d76s12 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d76!($v, scale 12 $(, $($rest)*)?) }; }
530/// `d76s18!(value)` — equivalent to `d76!(value, scale 18)`.
531#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))] #[macro_export]
532macro_rules! d76s18 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d76!($v, scale 18 $(, $($rest)*)?) }; }
533/// `d76s35!(value)` — equivalent to `d76!(value, scale 35)`.
534#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))] #[macro_export]
535macro_rules! d76s35 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d76!($v, scale 35 $(, $($rest)*)?) }; }
536/// `d76s50!(value)` — equivalent to `d76!(value, scale 50)`.
537#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))] #[macro_export]
538macro_rules! d76s50 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d76!($v, scale 50 $(, $($rest)*)?) }; }
539/// `d76s76!(value)` — equivalent to `d76!(value, scale 76)`.
540#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))] #[macro_export]
541macro_rules! d76s76 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d76!($v, scale 76 $(, $($rest)*)?) }; }
542
543// D153 curated scales.
544/// `d153s0!(value)` — equivalent to `d153!(value, scale 0)`.
545#[cfg(all(feature = "macros", any(feature = "d153", feature = "wide")))] #[macro_export]
546macro_rules! d153s0   { ($v:tt $(, $($rest:tt)*)?) => { $crate::d153!($v, scale 0   $(, $($rest)*)?) }; }
547/// `d153s35!(value)` — equivalent to `d153!(value, scale 35)`.
548#[cfg(all(feature = "macros", any(feature = "d153", feature = "wide")))] #[macro_export]
549macro_rules! d153s35  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d153!($v, scale 35  $(, $($rest)*)?) }; }
550/// `d153s75!(value)` — equivalent to `d153!(value, scale 75)`.
551#[cfg(all(feature = "macros", any(feature = "d153", feature = "wide")))] #[macro_export]
552macro_rules! d153s75  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d153!($v, scale 75  $(, $($rest)*)?) }; }
553/// `d153s150!(value)` — equivalent to `d153!(value, scale 150)`.
554#[cfg(all(feature = "macros", any(feature = "d153", feature = "wide")))] #[macro_export]
555macro_rules! d153s150 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d153!($v, scale 150 $(, $($rest)*)?) }; }
556/// `d153s153!(value)` — equivalent to `d153!(value, scale 153)`.
557#[cfg(all(feature = "macros", any(feature = "d153", feature = "wide")))] #[macro_export]
558macro_rules! d153s153 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d153!($v, scale 153 $(, $($rest)*)?) }; }
559
560// D307 curated scales.
561/// `d307s0!(value)` — equivalent to `d307!(value, scale 0)`.
562#[cfg(all(feature = "macros", any(feature = "d307", feature = "wide", feature = "x-wide")))] #[macro_export]
563macro_rules! d307s0   { ($v:tt $(, $($rest:tt)*)?) => { $crate::d307!($v, scale 0   $(, $($rest)*)?) }; }
564/// `d307s35!(value)` — equivalent to `d307!(value, scale 35)`.
565#[cfg(all(feature = "macros", any(feature = "d307", feature = "wide", feature = "x-wide")))] #[macro_export]
566macro_rules! d307s35  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d307!($v, scale 35  $(, $($rest)*)?) }; }
567/// `d307s150!(value)` — equivalent to `d307!(value, scale 150)`.
568#[cfg(all(feature = "macros", any(feature = "d307", feature = "wide", feature = "x-wide")))] #[macro_export]
569macro_rules! d307s150 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d307!($v, scale 150 $(, $($rest)*)?) }; }
570/// `d307s300!(value)` — equivalent to `d307!(value, scale 300)`.
571#[cfg(all(feature = "macros", any(feature = "d307", feature = "wide", feature = "x-wide")))] #[macro_export]
572macro_rules! d307s300 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d307!($v, scale 300 $(, $($rest)*)?) }; }
573/// `d307s307!(value)` — equivalent to `d307!(value, scale 307)`.
574#[cfg(all(feature = "macros", any(feature = "d307", feature = "wide", feature = "x-wide")))] #[macro_export]
575macro_rules! d307s307 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d307!($v, scale 307 $(, $($rest)*)?) }; }