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//! | [`D57<SCALE>`]   | 192-bit   |   57 | `d57` or `wide` |
22//! | [`D76<SCALE>`]   | 256-bit   |   76 | `d76` or `wide` |
23//! | [`D115<SCALE>`]  | 384-bit   |  115 | `d115` 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//! | [`D462<SCALE>`]  | 1536-bit  |  462 | `d462` or `x-wide` |
28//! | [`D616<SCALE>`]  | 2048-bit  |  616 | `d616` or `x-wide` |
29//! | [`D924<SCALE>`]  | 3072-bit  |  924 | `d924` or `xx-wide` |
30//! | [`D1232<SCALE>`] | 4096-bit  | 1232 | `d1232` or `xx-wide` |
31//!
32//! Umbrellas: `wide` enables D57 / D76 / D115 / D153 / D230 / D307;
33//! `x-wide` adds D462 + D616; `xx-wide` adds D924 + D1232. 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 algos;
188mod identity;
189mod support;
190mod types;
191#[cfg(feature = "bench-alt")]
192#[doc(hidden)]
193pub mod __bench_internals {
194    #[inline(never)]
195    pub fn limbs_mul(a: &[u128], b: &[u128], out: &mut [u128]) {
196        crate::wide_int::limbs_mul(a, b, out)
197    }
198    #[inline(never)]
199    pub fn limbs_mul_fast(a: &[u128], b: &[u128], out: &mut [u128]) {
200        crate::wide_int::limbs_mul_fast(a, b, out)
201    }
202    #[inline(never)]
203    pub fn mul_slice(a: &[u64], b: &[u64], out: &mut [u64]) {
204        crate::wide_int::limbs_mul_u64(a, b, out)
205    }
206    #[inline(never)]
207    pub fn mul_fixed<const L: usize, const D: usize>(
208        a: &[u64; L],
209        b: &[u64; L],
210        out: &mut [u64; D],
211    ) {
212        crate::wide_int::limbs_mul_u64_fixed::<L, D>(a, b, out)
213    }
214}
215mod macros;
216
217// `bitwise` and `num_traits_impls` used to live here as test-only
218// modules; their tests now run as Cargo integration tests under
219// `tests/`. The macro-generated impls themselves are emitted by
220// `decl_decimal_bitwise!` / `decl_decimal_num_traits_basics!` from
221// `types/widths.rs`, alongside every other surface.
222//
223// `wide_int` is unconditional. D38's strict transcendentals use
224// `Int512` as their guard-digit work integer (replacing the previous
225// `algos::fixed_d38::Fixed` 256-bit sign-magnitude type), so the wide-
226// integer family must be available in every feature configuration —
227// not just `feature = "wide"` builds. Compile-time impact is modest:
228// ~2k LOC of self-contained limb arithmetic plus the per-width
229// `decl_wide_int!` instantiations.
230mod wide_int;
231mod policy;
232
233#[cfg(feature = "serde")]
234pub use crate::support::serde_helpers;
235
236
237pub use crate::types::consts::DecimalConstants;
238pub use crate::types::traits::DecimalArithmetic;
239pub use crate::types::traits::DecimalConvert;
240pub use crate::types::unified::D;
241pub use crate::types::traits::Decimal;
242pub use crate::support::error::{ConvertError, ParseError};
243pub use crate::support::rounding::RoundingMode;
244pub use crate::types::traits::DecimalTranscendental;
245
246#[cfg(feature = "dyn")]
247pub use crate::types::traits::dyn_decimal::{DecimalWidth, DynDecimal, RawStorage};
248
249// D38 — the 128-bit foundation, plus every scale alias D38s0..=D38s37
250// (v0.4.0 cap: MAX_SCALE = name - 1).
251pub use crate::types::widths::{
252    D38, D38s0, D38s1, D38s2, D38s3, D38s4, D38s5, D38s6, D38s7, D38s8, D38s9, D38s10,
253    D38s11, D38s12, D38s13, D38s14, D38s15, D38s16, D38s17, D38s18, D38s19, D38s20,
254    D38s21, D38s22, D38s23, D38s24, D38s25, D38s26, D38s27, D38s28, D38s29, D38s30,
255    D38s31, D38s32, D38s33, D38s34, D38s35, D38s36, D38s37,
256};
257
258// D9 — 32-bit storage, scale 0..=9.
259pub use crate::types::widths::{
260    D9, D9s0, D9s1, D9s2, D9s3, D9s4, D9s5, D9s6, D9s7, D9s8,
261};
262
263// D18 — 64-bit storage, scale 0..=18.
264pub use crate::types::widths::{
265    D18, D18s0, D18s1, D18s2, D18s3, D18s4, D18s5, D18s6, D18s7, D18s8, D18s9, D18s10, D18s11,
266    D18s12, D18s13, D18s14, D18s15, D18s16, D18s17,
267};
268
269// D76 — 256-bit storage, behind the `d76` / `wide` features.
270#[cfg(any(feature = "d76", feature = "wide"))]
271pub use crate::types::widths::{
272    D76,
273    D76s0, D76s1, D76s2, D76s3, D76s4, D76s6, D76s9, D76s12, D76s15,
274    D76s18, D76s20, D76s24, D76s28, D76s32, D76s35, D76s38, D76s42,
275    D76s48, D76s50, D76s56, D76s64, D76s70, D76s75,
276};
277
278// The hand-rolled wide-integer types — the storage backend for the
279// wide decimal tiers, also useful on their own.
280#[cfg(any(feature = "d76", feature = "d153", feature = "d307", feature = "wide"))]
281pub use wide_int::{
282    Int256, Int512, Int1024, Int2048, Int4096, Uint256, Uint512, Uint1024, Uint2048, Uint4096,
283};
284
285// D153 — 512-bit storage, behind the `d153` / `wide` features.
286#[cfg(any(feature = "d153", feature = "wide"))]
287pub use crate::types::widths::{
288    D153,
289    D153s0, D153s1, D153s2, D153s4, D153s6, D153s9, D153s12, D153s15,
290    D153s18, D153s20, D153s24, D153s28, D153s32, D153s35, D153s38,
291    D153s50, D153s57, D153s75, D153s76, D153s100, D153s115, D153s140,
292    D153s150, D153s152,
293};
294
295// D307 — 1024-bit storage, behind the `d307` / `wide` features.
296#[cfg(any(feature = "d307", feature = "wide"))]
297pub use crate::types::widths::{
298    D307,
299    D307s0, D307s1, D307s2, D307s4, D307s6, D307s9, D307s12, D307s15,
300    D307s18, D307s20, D307s24, D307s28, D307s32, D307s35, D307s38,
301    D307s50, D307s75, D307s100, D307s115, D307s150, D307s153,
302    D307s200, D307s230, D307s275, D307s300, D307s306,
303};
304
305// ─── New half-width and wider tiers ───────────────────────────────────
306
307// D57 — 192-bit storage; half-width between D38 and D76.
308#[cfg(any(feature = "d57", feature = "wide"))]
309pub use crate::types::widths::{
310    D57,
311    D57s0, D57s1, D57s2, D57s4, D57s6, D57s9, D57s12, D57s18, D57s20, D57s24,
312    D57s28, D57s32, D57s38, D57s42, D57s48, D57s52, D57s56,
313};
314#[cfg(any(feature = "d57", feature = "wide"))]
315pub use wide_int::{Int192, Uint192};
316
317// D115 — 384-bit; half-width between D76 and D153.
318#[cfg(any(feature = "d115", feature = "wide"))]
319pub use crate::types::widths::{
320    D115,
321    D115s0, D115s1, D115s4, D115s8, D115s16, D115s24, D115s32, D115s38, D115s50,
322    D115s57, D115s64, D115s76, D115s90, D115s100, D115s110, D115s114,
323};
324#[cfg(any(feature = "d115", feature = "wide"))]
325pub use wide_int::{Int384, Uint384};
326
327// D230 — 768-bit; half-width between D153 and D307.
328#[cfg(any(feature = "d230", feature = "wide"))]
329pub use crate::types::widths::{
330    D230,
331    D230s0, D230s1, D230s6, D230s18, D230s38, D230s57, D230s75, D230s100, D230s115,
332    D230s140, D230s153, D230s175, D230s200, D230s215, D230s225, D230s229,
333};
334#[cfg(any(feature = "d230", feature = "wide"))]
335pub use wide_int::{Int768, Uint768};
336
337// D462 — 1536-bit; half-width between D307 and D616.
338#[cfg(any(feature = "d462", feature = "x-wide"))]
339pub use crate::types::widths::{
340    D462,
341    D462s0, D462s1, D462s18, D462s38, D462s75, D462s115, D462s153, D462s200, D462s230,
342    D462s275, D462s307, D462s350, D462s400, D462s440, D462s460, D462s461,
343};
344#[cfg(any(feature = "d462", feature = "x-wide"))]
345pub use wide_int::{Int1536, Uint1536};
346
347// D616 — 2048-bit; new top wide tier. Int2048 / Uint2048 are
348// already exported above for x-wide / d307 widening; no re-export
349// here.
350#[cfg(any(feature = "d616", feature = "x-wide"))]
351pub use crate::types::widths::{
352    D616,
353    D616s0, D616s1, D616s38, D616s75, D616s115, D616s153, D616s200, D616s230, D616s275,
354    D616s308, D616s380, D616s462, D616s500, D616s555, D616s600, D616s615,
355};
356
357// D924 — 3072-bit; half-width between D616 and D1232.
358#[cfg(any(feature = "d924", feature = "xx-wide"))]
359pub use crate::types::widths::{
360    D924,
361    D924s0, D924s1, D924s75, D924s153, D924s230, D924s307, D924s400, D924s461, D924s462,
362    D924s500, D924s616, D924s700, D924s800, D924s860, D924s900, D924s920, D924s923,
363};
364#[cfg(any(feature = "d924", feature = "xx-wide"))]
365pub use wide_int::{Int3072, Int6144, Int12288, Uint3072, Uint6144, Uint12288};
366
367// D1232 — 4096-bit; widest tier shipped.
368#[cfg(any(feature = "d1232", feature = "xx-wide"))]
369pub use crate::types::widths::{
370    D1232,
371    D1232s0, D1232s1, D1232s75, D1232s153, D1232s230, D1232s307, D1232s461, D1232s616,
372    D1232s700, D1232s800, D1232s900, D1232s924, D1232s1000, D1232s1100,
373    D1232s1180, D1232s1220, D1232s1230, D1232s1231,
374};
375#[cfg(any(feature = "d1232", feature = "xx-wide"))]
376pub use wide_int::{Int8192, Int16384, Uint8192, Uint16384};
377
378// ─── Construction macros (re-exports + per-scale wrappers) ────────────
379
380/// The narrow-tier proc-macros are always available with the
381/// `macros` feature; the wide-tier proc-macros are additionally
382/// feature-gated to match their target type's availability.
383#[cfg(feature = "macros")]
384pub use decimal_scaled_macros::{d9, d18, d38};
385
386#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))]
387pub use decimal_scaled_macros::d76;
388
389#[cfg(all(feature = "macros", any(feature = "d153", feature = "wide")))]
390pub use decimal_scaled_macros::d153;
391
392#[cfg(all(feature = "macros", any(feature = "d307", feature = "wide", feature = "x-wide")))]
393pub use decimal_scaled_macros::d307;
394
395#[cfg(all(feature = "macros", any(feature = "d57", feature = "wide")))]
396pub use decimal_scaled_macros::d57;
397
398#[cfg(all(feature = "macros", any(feature = "d115", feature = "wide")))]
399pub use decimal_scaled_macros::d115;
400
401#[cfg(all(feature = "macros", any(feature = "d230", feature = "wide")))]
402pub use decimal_scaled_macros::d230;
403
404#[cfg(all(feature = "macros", any(feature = "d462", feature = "x-wide")))]
405pub use decimal_scaled_macros::d462;
406
407#[cfg(all(feature = "macros", any(feature = "d616", feature = "x-wide")))]
408pub use decimal_scaled_macros::d616;
409
410#[cfg(all(feature = "macros", any(feature = "d924", feature = "xx-wide")))]
411pub use decimal_scaled_macros::d924;
412
413#[cfg(all(feature = "macros", any(feature = "d1232", feature = "xx-wide")))]
414pub use decimal_scaled_macros::d1232;
415
416// Per-scale wrappers — curated subset of pre-baked
417// `<dN>s<SCALE>!` macros that forward to the corresponding
418// proc-macro with `scale N` added. Long-tail scales remain
419// reachable via the explicit `, scale N` qualifier.
420//
421// Each alias is a tiny `macro_rules!`. We don't generate them
422// through a nested macro because `macro_rules!` doesn't support
423// directly emitting another `macro_rules!` without `$$` escapes
424// that aren't available in stable Rust; explicit per-line
425// declarations keep things debuggable and only cost ~40 lines.
426
427// D9 curated scales.
428/// `d9s0!(value)` — equivalent to `d9!(value, scale 0)`.
429#[cfg(feature = "macros")] #[macro_export]
430macro_rules! d9s0  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d9!($v, scale 0  $(, $($rest)*)?) }; }
431/// `d9s2!(value)` — equivalent to `d9!(value, scale 2)`.
432#[cfg(feature = "macros")] #[macro_export]
433macro_rules! d9s2  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d9!($v, scale 2  $(, $($rest)*)?) }; }
434/// `d9s4!(value)` — equivalent to `d9!(value, scale 4)`.
435#[cfg(feature = "macros")] #[macro_export]
436macro_rules! d9s4  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d9!($v, scale 4  $(, $($rest)*)?) }; }
437/// `d9s6!(value)` — equivalent to `d9!(value, scale 6)`.
438#[cfg(feature = "macros")] #[macro_export]
439macro_rules! d9s6  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d9!($v, scale 6  $(, $($rest)*)?) }; }
440
441// D18 curated scales.
442/// `d18s0!(value)` — equivalent to `d18!(value, scale 0)`.
443#[cfg(feature = "macros")] #[macro_export]
444macro_rules! d18s0  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d18!($v, scale 0  $(, $($rest)*)?) }; }
445/// `d18s2!(value)` — equivalent to `d18!(value, scale 2)`.
446#[cfg(feature = "macros")] #[macro_export]
447macro_rules! d18s2  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d18!($v, scale 2  $(, $($rest)*)?) }; }
448/// `d18s4!(value)` — equivalent to `d18!(value, scale 4)`.
449#[cfg(feature = "macros")] #[macro_export]
450macro_rules! d18s4  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d18!($v, scale 4  $(, $($rest)*)?) }; }
451/// `d18s6!(value)` — equivalent to `d18!(value, scale 6)`.
452#[cfg(feature = "macros")] #[macro_export]
453macro_rules! d18s6  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d18!($v, scale 6  $(, $($rest)*)?) }; }
454/// `d18s9!(value)` — equivalent to `d18!(value, scale 9)`.
455#[cfg(feature = "macros")] #[macro_export]
456macro_rules! d18s9  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d18!($v, scale 9  $(, $($rest)*)?) }; }
457/// `d18s12!(value)` — equivalent to `d18!(value, scale 12)`.
458#[cfg(feature = "macros")] #[macro_export]
459macro_rules! d18s12 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d18!($v, scale 12 $(, $($rest)*)?) }; }
460
461// D38 curated scales.
462/// `d38s0!(value)` — equivalent to `d38!(value, scale 0)`.
463#[cfg(feature = "macros")] #[macro_export]
464macro_rules! d38s0  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 0  $(, $($rest)*)?) }; }
465/// `d38s2!(value)` — equivalent to `d38!(value, scale 2)`.
466#[cfg(feature = "macros")] #[macro_export]
467macro_rules! d38s2  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 2  $(, $($rest)*)?) }; }
468/// `d38s4!(value)` — equivalent to `d38!(value, scale 4)`.
469#[cfg(feature = "macros")] #[macro_export]
470macro_rules! d38s4  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 4  $(, $($rest)*)?) }; }
471/// `d38s6!(value)` — equivalent to `d38!(value, scale 6)`.
472#[cfg(feature = "macros")] #[macro_export]
473macro_rules! d38s6  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 6  $(, $($rest)*)?) }; }
474/// `d38s8!(value)` — equivalent to `d38!(value, scale 8)`.
475#[cfg(feature = "macros")] #[macro_export]
476macro_rules! d38s8  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 8  $(, $($rest)*)?) }; }
477/// `d38s9!(value)` — equivalent to `d38!(value, scale 9)`.
478#[cfg(feature = "macros")] #[macro_export]
479macro_rules! d38s9  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 9  $(, $($rest)*)?) }; }
480/// `d38s12!(value)` — equivalent to `d38!(value, scale 12)`.
481#[cfg(feature = "macros")] #[macro_export]
482macro_rules! d38s12 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 12 $(, $($rest)*)?) }; }
483/// `d38s15!(value)` — equivalent to `d38!(value, scale 15)`.
484#[cfg(feature = "macros")] #[macro_export]
485macro_rules! d38s15 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 15 $(, $($rest)*)?) }; }
486/// `d38s18!(value)` — equivalent to `d38!(value, scale 18)`.
487#[cfg(feature = "macros")] #[macro_export]
488macro_rules! d38s18 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 18 $(, $($rest)*)?) }; }
489/// `d38s24!(value)` — equivalent to `d38!(value, scale 24)`.
490#[cfg(feature = "macros")] #[macro_export]
491macro_rules! d38s24 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 24 $(, $($rest)*)?) }; }
492/// `d38s35!(value)` — equivalent to `d38!(value, scale 35)`.
493#[cfg(feature = "macros")] #[macro_export]
494macro_rules! d38s35 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 35 $(, $($rest)*)?) }; }
495
496// D76 curated scales.
497/// `d76s0!(value)` — equivalent to `d76!(value, scale 0)`.
498#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))] #[macro_export]
499macro_rules! d76s0  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d76!($v, scale 0  $(, $($rest)*)?) }; }
500/// `d76s2!(value)` — equivalent to `d76!(value, scale 2)`.
501#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))] #[macro_export]
502macro_rules! d76s2  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d76!($v, scale 2  $(, $($rest)*)?) }; }
503/// `d76s6!(value)` — equivalent to `d76!(value, scale 6)`.
504#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))] #[macro_export]
505macro_rules! d76s6  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d76!($v, scale 6  $(, $($rest)*)?) }; }
506/// `d76s12!(value)` — equivalent to `d76!(value, scale 12)`.
507#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))] #[macro_export]
508macro_rules! d76s12 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d76!($v, scale 12 $(, $($rest)*)?) }; }
509/// `d76s18!(value)` — equivalent to `d76!(value, scale 18)`.
510#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))] #[macro_export]
511macro_rules! d76s18 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d76!($v, scale 18 $(, $($rest)*)?) }; }
512/// `d76s35!(value)` — equivalent to `d76!(value, scale 35)`.
513#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))] #[macro_export]
514macro_rules! d76s35 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d76!($v, scale 35 $(, $($rest)*)?) }; }
515/// `d76s50!(value)` — equivalent to `d76!(value, scale 50)`.
516#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))] #[macro_export]
517macro_rules! d76s50 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d76!($v, scale 50 $(, $($rest)*)?) }; }
518
519// D153 curated scales.
520/// `d153s0!(value)` — equivalent to `d153!(value, scale 0)`.
521#[cfg(all(feature = "macros", any(feature = "d153", feature = "wide")))] #[macro_export]
522macro_rules! d153s0   { ($v:tt $(, $($rest:tt)*)?) => { $crate::d153!($v, scale 0   $(, $($rest)*)?) }; }
523/// `d153s35!(value)` — equivalent to `d153!(value, scale 35)`.
524#[cfg(all(feature = "macros", any(feature = "d153", feature = "wide")))] #[macro_export]
525macro_rules! d153s35  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d153!($v, scale 35  $(, $($rest)*)?) }; }
526/// `d153s75!(value)` — equivalent to `d153!(value, scale 75)`.
527#[cfg(all(feature = "macros", any(feature = "d153", feature = "wide")))] #[macro_export]
528macro_rules! d153s75  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d153!($v, scale 75  $(, $($rest)*)?) }; }
529/// `d153s150!(value)` — equivalent to `d153!(value, scale 150)`.
530#[cfg(all(feature = "macros", any(feature = "d153", feature = "wide")))] #[macro_export]
531macro_rules! d153s150 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d153!($v, scale 150 $(, $($rest)*)?) }; }
532
533// D307 curated scales.
534/// `d307s0!(value)` — equivalent to `d307!(value, scale 0)`.
535#[cfg(all(feature = "macros", any(feature = "d307", feature = "wide", feature = "x-wide")))] #[macro_export]
536macro_rules! d307s0   { ($v:tt $(, $($rest:tt)*)?) => { $crate::d307!($v, scale 0   $(, $($rest)*)?) }; }
537/// `d307s35!(value)` — equivalent to `d307!(value, scale 35)`.
538#[cfg(all(feature = "macros", any(feature = "d307", feature = "wide", feature = "x-wide")))] #[macro_export]
539macro_rules! d307s35  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d307!($v, scale 35  $(, $($rest)*)?) }; }
540/// `d307s150!(value)` — equivalent to `d307!(value, scale 150)`.
541#[cfg(all(feature = "macros", any(feature = "d307", feature = "wide", feature = "x-wide")))] #[macro_export]
542macro_rules! d307s150 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d307!($v, scale 150 $(, $($rest)*)?) }; }
543/// `d307s300!(value)` — equivalent to `d307!(value, scale 300)`.
544#[cfg(all(feature = "macros", any(feature = "d307", feature = "wide", feature = "x-wide")))] #[macro_export]
545macro_rules! d307s300 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d307!($v, scale 300 $(, $($rest)*)?) }; }