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;
255mod transcendental_trait;
256mod arithmetic_trait;
257mod convert_trait;
258
259
260pub use consts::DecimalConstants;
261#[allow(deprecated)]
262pub use consts::DecimalConsts;
263pub use arithmetic_trait::DecimalArithmetic;
264pub use convert_trait::DecimalConvert;
265pub use decimal_trait::Decimal;
266pub use error::{ConvertError, ParseError};
267pub use rounding::RoundingMode;
268pub use transcendental_trait::DecimalTranscendental;
269
270// D38 — the 128-bit foundation, plus every scale alias D38s0..=D38s38.
271pub use core_type::{
272    D38, D38s0, D38s1, D38s2, D38s3, D38s4, D38s5, D38s6, D38s7, D38s8, D38s9, D38s10,
273    D38s11, D38s12, D38s13, D38s14, D38s15, D38s16, D38s17, D38s18, D38s19, D38s20,
274    D38s21, D38s22, D38s23, D38s24, D38s25, D38s26, D38s27, D38s28, D38s29, D38s30,
275    D38s31, D38s32, D38s33, D38s34, D38s35, D38s36, D38s37, D38s38,
276};
277
278// D9 — 32-bit storage, scale 0..=9.
279pub use core_type::{
280    D9, D9s0, D9s1, D9s2, D9s3, D9s4, D9s5, D9s6, D9s7, D9s8, D9s9,
281};
282
283// D18 — 64-bit storage, scale 0..=18.
284pub use core_type::{
285    D18, D18s0, D18s1, D18s2, D18s3, D18s4, D18s5, D18s6, D18s7, D18s8, D18s9, D18s10, D18s11,
286    D18s12, D18s13, D18s14, D18s15, D18s16, D18s17, D18s18,
287};
288
289// D76 — 256-bit storage, behind the `d76` / `wide` features.
290#[cfg(any(feature = "d76", feature = "wide"))]
291pub use core_type::{
292    D76,
293    D76s0, D76s1, D76s2, D76s3, D76s4, D76s6, D76s9, D76s12, D76s15,
294    D76s18, D76s20, D76s24, D76s28, D76s32, D76s35, D76s38, D76s42,
295    D76s48, D76s50, D76s56, D76s64, D76s70, D76s75, D76s76,
296};
297
298// The hand-rolled wide-integer types — the storage backend for the
299// wide decimal tiers, also useful on their own.
300#[cfg(any(feature = "d76", feature = "d153", feature = "d307", feature = "wide"))]
301pub use wide_int::{
302    Int256, Int512, Int1024, Int2048, Int4096, Uint256, Uint512, Uint1024, Uint2048, Uint4096,
303};
304
305// D153 — 512-bit storage, behind the `d153` / `wide` features.
306#[cfg(any(feature = "d153", feature = "wide"))]
307pub use core_type::{
308    D153,
309    D153s0, D153s1, D153s2, D153s4, D153s6, D153s9, D153s12, D153s15,
310    D153s18, D153s20, D153s24, D153s28, D153s32, D153s35, D153s38,
311    D153s50, D153s57, D153s75, D153s76, D153s100, D153s115, D153s140,
312    D153s150, D153s152, D153s153,
313};
314
315// D307 — 1024-bit storage, behind the `d307` / `wide` features.
316#[cfg(any(feature = "d307", feature = "wide"))]
317pub use core_type::{
318    D307,
319    D307s0, D307s1, D307s2, D307s4, D307s6, D307s9, D307s12, D307s15,
320    D307s18, D307s20, D307s24, D307s28, D307s32, D307s35, D307s38,
321    D307s50, D307s75, D307s100, D307s115, D307s150, D307s153,
322    D307s200, D307s230, D307s275, D307s300, D307s306, D307s307,
323};
324
325// ─── New half-width and wider tiers ───────────────────────────────────
326
327// D56 — 192-bit storage; half-width between D38 and D76.
328#[cfg(any(feature = "d56", feature = "wide"))]
329pub use core_type::{
330    D56,
331    D56s0, D56s1, D56s2, D56s4, D56s6, D56s9, D56s12, D56s18, D56s20, D56s24,
332    D56s28, D56s32, D56s38, D56s42, D56s48, D56s52, D56s56, D56s57,
333};
334#[cfg(any(feature = "d56", feature = "wide"))]
335pub use wide_int::{Int192, Uint192};
336
337// D114 — 384-bit; half-width between D76 and D153.
338#[cfg(any(feature = "d114", feature = "wide"))]
339pub use core_type::{
340    D114,
341    D114s0, D114s1, D114s4, D114s8, D114s16, D114s24, D114s32, D114s38, D114s50,
342    D114s57, D114s64, D114s76, D114s90, D114s100, D114s110, D114s114, D114s115,
343};
344#[cfg(any(feature = "d114", feature = "wide"))]
345pub use wide_int::{Int384, Uint384};
346
347// D230 — 768-bit; half-width between D153 and D307.
348#[cfg(any(feature = "d230", feature = "wide"))]
349pub use core_type::{
350    D230,
351    D230s0, D230s1, D230s6, D230s18, D230s38, D230s57, D230s75, D230s100, D230s115,
352    D230s140, D230s153, D230s175, D230s200, D230s215, D230s225, D230s229, D230s230,
353};
354#[cfg(any(feature = "d230", feature = "wide"))]
355pub use wide_int::{Int768, Uint768};
356
357// D461 — 1536-bit; half-width between D307 and D615.
358#[cfg(any(feature = "d461", feature = "x-wide"))]
359pub use core_type::{
360    D461,
361    D461s0, D461s1, D461s18, D461s38, D461s75, D461s115, D461s153, D461s200, D461s230,
362    D461s275, D461s307, D461s350, D461s400, D461s440, D461s460, D461s461, D461s462,
363};
364#[cfg(any(feature = "d461", feature = "x-wide"))]
365pub use wide_int::{Int1536, Uint1536};
366
367// D615 — 2048-bit; new top wide tier. Int2048 / Uint2048 are
368// already exported above for x-wide / d307 widening; no re-export
369// here.
370#[cfg(any(feature = "d615", feature = "x-wide"))]
371pub use core_type::{
372    D615,
373    D615s0, D615s1, D615s38, D615s75, D615s115, D615s153, D615s200, D615s230, D615s275,
374    D615s308, D615s380, D615s462, D615s500, D615s555, D615s600, D615s615, D615s616,
375};
376
377// D923 — 3072-bit; half-width between D615 and D1231.
378#[cfg(any(feature = "d923", feature = "xx-wide"))]
379pub use core_type::{
380    D923,
381    D923s0, D923s1, D923s75, D923s153, D923s230, D923s307, D923s400, D923s461, D923s462,
382    D923s500, D923s616, D923s700, D923s800, D923s860, D923s900, D923s920, D923s923, D923s924,
383};
384#[cfg(any(feature = "d923", feature = "xx-wide"))]
385pub use wide_int::{Int3072, Int6144, Int12288, Uint3072, Uint6144, Uint12288};
386
387// D1231 — 4096-bit; widest tier shipped.
388#[cfg(any(feature = "d1231", feature = "xx-wide"))]
389pub use core_type::{
390    D1231,
391    D1231s0, D1231s1, D1231s75, D1231s153, D1231s230, D1231s307, D1231s461, D1231s616,
392    D1231s700, D1231s800, D1231s900, D1231s924, D1231s1000, D1231s1100,
393    D1231s1180, D1231s1220, D1231s1230, D1231s1231, D1231s1232,
394};
395#[cfg(any(feature = "d1231", feature = "xx-wide"))]
396pub use wide_int::{Int8192, Int16384, Uint8192, Uint16384};
397
398// ─── Construction macros (re-exports + per-scale wrappers) ────────────
399
400/// The narrow-tier proc-macros are always available with the
401/// `macros` feature; the wide-tier proc-macros are additionally
402/// feature-gated to match their target type's availability.
403#[cfg(feature = "macros")]
404pub use decimal_scaled_macros::{d9, d18, d38};
405
406#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))]
407pub use decimal_scaled_macros::d76;
408
409#[cfg(all(feature = "macros", any(feature = "d153", feature = "wide")))]
410pub use decimal_scaled_macros::d153;
411
412#[cfg(all(feature = "macros", any(feature = "d307", feature = "wide", feature = "x-wide")))]
413pub use decimal_scaled_macros::d307;
414
415#[cfg(all(feature = "macros", any(feature = "d56", feature = "wide")))]
416pub use decimal_scaled_macros::d56;
417
418#[cfg(all(feature = "macros", any(feature = "d114", feature = "wide")))]
419pub use decimal_scaled_macros::d114;
420
421#[cfg(all(feature = "macros", any(feature = "d230", feature = "wide")))]
422pub use decimal_scaled_macros::d230;
423
424#[cfg(all(feature = "macros", any(feature = "d461", feature = "x-wide")))]
425pub use decimal_scaled_macros::d461;
426
427#[cfg(all(feature = "macros", any(feature = "d615", feature = "x-wide")))]
428pub use decimal_scaled_macros::d615;
429
430#[cfg(all(feature = "macros", any(feature = "d923", feature = "xx-wide")))]
431pub use decimal_scaled_macros::d923;
432
433#[cfg(all(feature = "macros", any(feature = "d1231", feature = "xx-wide")))]
434pub use decimal_scaled_macros::d1231;
435
436// Per-scale wrappers — curated subset of pre-baked
437// `<dN>s<SCALE>!` macros that forward to the corresponding
438// proc-macro with `scale N` added. Long-tail scales remain
439// reachable via the explicit `, scale N` qualifier.
440//
441// Each alias is a tiny `macro_rules!`. We don't generate them
442// through a nested macro because `macro_rules!` doesn't support
443// directly emitting another `macro_rules!` without `$$` escapes
444// that aren't available in stable Rust; explicit per-line
445// declarations keep things debuggable and only cost ~40 lines.
446
447// D9 curated scales.
448/// `d9s0!(value)` — equivalent to `d9!(value, scale 0)`.
449#[cfg(feature = "macros")] #[macro_export]
450macro_rules! d9s0  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d9!($v, scale 0  $(, $($rest)*)?) }; }
451/// `d9s2!(value)` — equivalent to `d9!(value, scale 2)`.
452#[cfg(feature = "macros")] #[macro_export]
453macro_rules! d9s2  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d9!($v, scale 2  $(, $($rest)*)?) }; }
454/// `d9s4!(value)` — equivalent to `d9!(value, scale 4)`.
455#[cfg(feature = "macros")] #[macro_export]
456macro_rules! d9s4  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d9!($v, scale 4  $(, $($rest)*)?) }; }
457/// `d9s6!(value)` — equivalent to `d9!(value, scale 6)`.
458#[cfg(feature = "macros")] #[macro_export]
459macro_rules! d9s6  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d9!($v, scale 6  $(, $($rest)*)?) }; }
460/// `d9s9!(value)` — equivalent to `d9!(value, scale 9)`.
461#[cfg(feature = "macros")] #[macro_export]
462macro_rules! d9s9  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d9!($v, scale 9  $(, $($rest)*)?) }; }
463
464// D18 curated scales.
465/// `d18s0!(value)` — equivalent to `d18!(value, scale 0)`.
466#[cfg(feature = "macros")] #[macro_export]
467macro_rules! d18s0  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d18!($v, scale 0  $(, $($rest)*)?) }; }
468/// `d18s2!(value)` — equivalent to `d18!(value, scale 2)`.
469#[cfg(feature = "macros")] #[macro_export]
470macro_rules! d18s2  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d18!($v, scale 2  $(, $($rest)*)?) }; }
471/// `d18s4!(value)` — equivalent to `d18!(value, scale 4)`.
472#[cfg(feature = "macros")] #[macro_export]
473macro_rules! d18s4  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d18!($v, scale 4  $(, $($rest)*)?) }; }
474/// `d18s6!(value)` — equivalent to `d18!(value, scale 6)`.
475#[cfg(feature = "macros")] #[macro_export]
476macro_rules! d18s6  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d18!($v, scale 6  $(, $($rest)*)?) }; }
477/// `d18s9!(value)` — equivalent to `d18!(value, scale 9)`.
478#[cfg(feature = "macros")] #[macro_export]
479macro_rules! d18s9  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d18!($v, scale 9  $(, $($rest)*)?) }; }
480/// `d18s12!(value)` — equivalent to `d18!(value, scale 12)`.
481#[cfg(feature = "macros")] #[macro_export]
482macro_rules! d18s12 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d18!($v, scale 12 $(, $($rest)*)?) }; }
483/// `d18s18!(value)` — equivalent to `d18!(value, scale 18)`.
484#[cfg(feature = "macros")] #[macro_export]
485macro_rules! d18s18 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d18!($v, scale 18 $(, $($rest)*)?) }; }
486
487// D38 curated scales.
488/// `d38s0!(value)` — equivalent to `d38!(value, scale 0)`.
489#[cfg(feature = "macros")] #[macro_export]
490macro_rules! d38s0  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 0  $(, $($rest)*)?) }; }
491/// `d38s2!(value)` — equivalent to `d38!(value, scale 2)`.
492#[cfg(feature = "macros")] #[macro_export]
493macro_rules! d38s2  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 2  $(, $($rest)*)?) }; }
494/// `d38s4!(value)` — equivalent to `d38!(value, scale 4)`.
495#[cfg(feature = "macros")] #[macro_export]
496macro_rules! d38s4  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 4  $(, $($rest)*)?) }; }
497/// `d38s6!(value)` — equivalent to `d38!(value, scale 6)`.
498#[cfg(feature = "macros")] #[macro_export]
499macro_rules! d38s6  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 6  $(, $($rest)*)?) }; }
500/// `d38s8!(value)` — equivalent to `d38!(value, scale 8)`.
501#[cfg(feature = "macros")] #[macro_export]
502macro_rules! d38s8  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 8  $(, $($rest)*)?) }; }
503/// `d38s9!(value)` — equivalent to `d38!(value, scale 9)`.
504#[cfg(feature = "macros")] #[macro_export]
505macro_rules! d38s9  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 9  $(, $($rest)*)?) }; }
506/// `d38s12!(value)` — equivalent to `d38!(value, scale 12)`.
507#[cfg(feature = "macros")] #[macro_export]
508macro_rules! d38s12 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 12 $(, $($rest)*)?) }; }
509/// `d38s15!(value)` — equivalent to `d38!(value, scale 15)`.
510#[cfg(feature = "macros")] #[macro_export]
511macro_rules! d38s15 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 15 $(, $($rest)*)?) }; }
512/// `d38s18!(value)` — equivalent to `d38!(value, scale 18)`.
513#[cfg(feature = "macros")] #[macro_export]
514macro_rules! d38s18 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 18 $(, $($rest)*)?) }; }
515/// `d38s24!(value)` — equivalent to `d38!(value, scale 24)`.
516#[cfg(feature = "macros")] #[macro_export]
517macro_rules! d38s24 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 24 $(, $($rest)*)?) }; }
518/// `d38s35!(value)` — equivalent to `d38!(value, scale 35)`.
519#[cfg(feature = "macros")] #[macro_export]
520macro_rules! d38s35 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 35 $(, $($rest)*)?) }; }
521/// `d38s38!(value)` — equivalent to `d38!(value, scale 38)`.
522#[cfg(feature = "macros")] #[macro_export]
523macro_rules! d38s38 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 38 $(, $($rest)*)?) }; }
524
525// D76 curated scales.
526/// `d76s0!(value)` — equivalent to `d76!(value, scale 0)`.
527#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))] #[macro_export]
528macro_rules! d76s0  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d76!($v, scale 0  $(, $($rest)*)?) }; }
529/// `d76s2!(value)` — equivalent to `d76!(value, scale 2)`.
530#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))] #[macro_export]
531macro_rules! d76s2  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d76!($v, scale 2  $(, $($rest)*)?) }; }
532/// `d76s6!(value)` — equivalent to `d76!(value, scale 6)`.
533#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))] #[macro_export]
534macro_rules! d76s6  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d76!($v, scale 6  $(, $($rest)*)?) }; }
535/// `d76s12!(value)` — equivalent to `d76!(value, scale 12)`.
536#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))] #[macro_export]
537macro_rules! d76s12 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d76!($v, scale 12 $(, $($rest)*)?) }; }
538/// `d76s18!(value)` — equivalent to `d76!(value, scale 18)`.
539#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))] #[macro_export]
540macro_rules! d76s18 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d76!($v, scale 18 $(, $($rest)*)?) }; }
541/// `d76s35!(value)` — equivalent to `d76!(value, scale 35)`.
542#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))] #[macro_export]
543macro_rules! d76s35 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d76!($v, scale 35 $(, $($rest)*)?) }; }
544/// `d76s50!(value)` — equivalent to `d76!(value, scale 50)`.
545#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))] #[macro_export]
546macro_rules! d76s50 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d76!($v, scale 50 $(, $($rest)*)?) }; }
547/// `d76s76!(value)` — equivalent to `d76!(value, scale 76)`.
548#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))] #[macro_export]
549macro_rules! d76s76 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d76!($v, scale 76 $(, $($rest)*)?) }; }
550
551// D153 curated scales.
552/// `d153s0!(value)` — equivalent to `d153!(value, scale 0)`.
553#[cfg(all(feature = "macros", any(feature = "d153", feature = "wide")))] #[macro_export]
554macro_rules! d153s0   { ($v:tt $(, $($rest:tt)*)?) => { $crate::d153!($v, scale 0   $(, $($rest)*)?) }; }
555/// `d153s35!(value)` — equivalent to `d153!(value, scale 35)`.
556#[cfg(all(feature = "macros", any(feature = "d153", feature = "wide")))] #[macro_export]
557macro_rules! d153s35  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d153!($v, scale 35  $(, $($rest)*)?) }; }
558/// `d153s75!(value)` — equivalent to `d153!(value, scale 75)`.
559#[cfg(all(feature = "macros", any(feature = "d153", feature = "wide")))] #[macro_export]
560macro_rules! d153s75  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d153!($v, scale 75  $(, $($rest)*)?) }; }
561/// `d153s150!(value)` — equivalent to `d153!(value, scale 150)`.
562#[cfg(all(feature = "macros", any(feature = "d153", feature = "wide")))] #[macro_export]
563macro_rules! d153s150 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d153!($v, scale 150 $(, $($rest)*)?) }; }
564/// `d153s153!(value)` — equivalent to `d153!(value, scale 153)`.
565#[cfg(all(feature = "macros", any(feature = "d153", feature = "wide")))] #[macro_export]
566macro_rules! d153s153 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d153!($v, scale 153 $(, $($rest)*)?) }; }
567
568// D307 curated scales.
569/// `d307s0!(value)` — equivalent to `d307!(value, scale 0)`.
570#[cfg(all(feature = "macros", any(feature = "d307", feature = "wide", feature = "x-wide")))] #[macro_export]
571macro_rules! d307s0   { ($v:tt $(, $($rest:tt)*)?) => { $crate::d307!($v, scale 0   $(, $($rest)*)?) }; }
572/// `d307s35!(value)` — equivalent to `d307!(value, scale 35)`.
573#[cfg(all(feature = "macros", any(feature = "d307", feature = "wide", feature = "x-wide")))] #[macro_export]
574macro_rules! d307s35  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d307!($v, scale 35  $(, $($rest)*)?) }; }
575/// `d307s150!(value)` — equivalent to `d307!(value, scale 150)`.
576#[cfg(all(feature = "macros", any(feature = "d307", feature = "wide", feature = "x-wide")))] #[macro_export]
577macro_rules! d307s150 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d307!($v, scale 150 $(, $($rest)*)?) }; }
578/// `d307s300!(value)` — equivalent to `d307!(value, scale 300)`.
579#[cfg(all(feature = "macros", any(feature = "d307", feature = "wide", feature = "x-wide")))] #[macro_export]
580macro_rules! d307s300 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d307!($v, scale 300 $(, $($rest)*)?) }; }
581/// `d307s307!(value)` — equivalent to `d307!(value, scale 307)`.
582#[cfg(all(feature = "macros", any(feature = "d307", feature = "wide", feature = "x-wide")))] #[macro_export]
583macro_rules! d307s307 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d307!($v, scale 307 $(, $($rest)*)?) }; }