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