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
180mod arithmetic;
181#[cfg(feature = "bench-alt")]
182mod bench_alt;
183#[cfg(feature = "bench-alt")]
184#[doc(hidden)]
185pub mod __bench_internals {
186 #[inline(never)]
187 pub fn limbs_mul(a: &[u128], b: &[u128], out: &mut [u128]) {
188 crate::wide_int::limbs_mul(a, b, out)
189 }
190 #[inline(never)]
191 pub fn limbs_mul_fast(a: &[u128], b: &[u128], out: &mut [u128]) {
192 crate::wide_int::limbs_mul_fast(a, b, out)
193 }
194}
195mod consts;
196mod consts_wide;
197mod core_type;
198mod decimal_trait;
199mod display;
200mod equalities;
201mod error;
202mod macros;
203mod num_traits;
204mod log_exp_strict;
205mod log_exp_fast;
206
207// `bitwise` and `num_traits_impls` used to live here as test-only
208// modules; their tests now run as Cargo integration tests under
209// `tests/`. The macro-generated impls themselves are emitted by
210// `decl_decimal_bitwise!` / `decl_decimal_num_traits_basics!` from
211// `core_type.rs`, alongside every other surface.
212mod rescale;
213mod rounding;
214mod mg_divide;
215mod d_w128_kernels;
216// `wide_int` is now unconditional. D38's strict transcendentals use
217// `Int512` as their guard-digit work integer (replacing the previous
218// `d_w128_kernels::Fixed` 256-bit sign-magnitude type), so the wide-
219// integer family must be available in every feature configuration —
220// not just `feature = "wide"` builds. Compile-time impact is modest:
221// ~2k LOC of self-contained limb arithmetic plus the per-width
222// `decl_wide_int!` instantiations.
223mod wide_int;
224mod overflow_variants;
225mod powers_strict;
226mod powers_fast;
227
228#[cfg(feature = "serde")]
229pub mod serde_helpers;
230// `trig` is compiled when it has any surface to emit: the integer-only
231// `*_strict` methods (present unless `fast`) or the f64-bridge
232// methods (present with `std`).
233#[cfg(any(not(feature = "fast"), feature = "std"))]
234mod trig_strict;
235mod trig_fast;
236
237
238pub use consts::DecimalConsts;
239pub use decimal_trait::Decimal;
240pub use error::{ConvertError, ParseError};
241pub use rounding::RoundingMode;
242
243// D38 — the 128-bit foundation, plus every scale alias D38s0..=D38s38.
244pub use core_type::{
245 D38, D38s0, D38s1, D38s2, D38s3, D38s4, D38s5, D38s6, D38s7, D38s8, D38s9, D38s10,
246 D38s11, D38s12, D38s13, D38s14, D38s15, D38s16, D38s17, D38s18, D38s19, D38s20,
247 D38s21, D38s22, D38s23, D38s24, D38s25, D38s26, D38s27, D38s28, D38s29, D38s30,
248 D38s31, D38s32, D38s33, D38s34, D38s35, D38s36, D38s37, D38s38,
249};
250
251// D9 — 32-bit storage, scale 0..=9.
252pub use core_type::{
253 D9, D9s0, D9s1, D9s2, D9s3, D9s4, D9s5, D9s6, D9s7, D9s8, D9s9,
254};
255
256// D18 — 64-bit storage, scale 0..=18.
257pub use core_type::{
258 D18, D18s0, D18s1, D18s2, D18s3, D18s4, D18s5, D18s6, D18s7, D18s8, D18s9, D18s10, D18s11,
259 D18s12, D18s13, D18s14, D18s15, D18s16, D18s17, D18s18,
260};
261
262// D76 — 256-bit storage, behind the `d76` / `wide` features.
263#[cfg(any(feature = "d76", feature = "wide"))]
264pub use core_type::{
265 D76, D76s0, D76s2, D76s6, D76s12, D76s18, D76s35, D76s50, D76s76,
266};
267
268// The hand-rolled wide-integer types — the storage backend for the
269// wide decimal tiers, also useful on their own.
270#[cfg(any(feature = "d76", feature = "d153", feature = "d307", feature = "wide"))]
271pub use wide_int::{
272 Int256, Int512, Int1024, Int2048, Int4096, Uint256, Uint512, Uint1024, Uint2048, Uint4096,
273};
274
275// D153 — 512-bit storage, behind the `d153` / `wide` features.
276#[cfg(any(feature = "d153", feature = "wide"))]
277pub use core_type::{D153, D153s0, D153s35, D153s75, D153s150, D153s153};
278
279// D307 — 1024-bit storage, behind the `d307` / `wide` features.
280#[cfg(any(feature = "d307", feature = "wide"))]
281pub use core_type::{D307, D307s0, D307s35, D307s150, D307s300, D307s307};
282
283// ─── New half-width and wider tiers ───────────────────────────────────
284
285// D56 — 192-bit storage; half-width between D38 and D76.
286#[cfg(any(feature = "d56", feature = "wide"))]
287pub use core_type::{
288 D56,
289 D56s0, D56s2, D56s4, D56s6, D56s9, D56s12, D56s18, D56s24,
290 D56s28, D56s32, D56s38, D56s42, D56s48, D56s52, D56s56, D56s57,
291};
292#[cfg(any(feature = "d56", feature = "wide"))]
293pub use wide_int::{Int192, Uint192};
294
295// D114 — 384-bit; half-width between D76 and D153.
296#[cfg(any(feature = "d114", feature = "wide"))]
297pub use core_type::{
298 D114,
299 D114s0, D114s4, D114s8, D114s16, D114s24, D114s32, D114s38, D114s50,
300 D114s57, D114s64, D114s76, D114s90, D114s100, D114s110, D114s114, D114s115,
301};
302#[cfg(any(feature = "d114", feature = "wide"))]
303pub use wide_int::{Int384, Uint384};
304
305// D230 — 768-bit; half-width between D153 and D307.
306#[cfg(any(feature = "d230", feature = "wide"))]
307pub use core_type::{
308 D230,
309 D230s0, D230s6, D230s18, D230s38, D230s57, D230s75, D230s100, D230s115,
310 D230s140, D230s153, D230s175, D230s200, D230s215, D230s225, D230s229, D230s230,
311};
312#[cfg(any(feature = "d230", feature = "wide"))]
313pub use wide_int::{Int768, Uint768};
314
315// D461 — 1536-bit; half-width between D307 and D615.
316#[cfg(any(feature = "d461", feature = "x-wide"))]
317pub use core_type::{
318 D461,
319 D461s0, D461s18, D461s38, D461s75, D461s115, D461s153, D461s200, D461s230,
320 D461s275, D461s307, D461s350, D461s400, D461s440, D461s460, D461s461, D461s462,
321};
322#[cfg(any(feature = "d461", feature = "x-wide"))]
323pub use wide_int::{Int1536, Uint1536};
324
325// D615 — 2048-bit; new top wide tier. Int2048 / Uint2048 are
326// already exported above for x-wide / d307 widening; no re-export
327// here.
328#[cfg(any(feature = "d615", feature = "x-wide"))]
329pub use core_type::{
330 D615,
331 D615s0, D615s38, D615s75, D615s115, D615s153, D615s200, D615s230, D615s275,
332 D615s308, D615s380, D615s462, D615s500, D615s555, D615s600, D615s615, D615s616,
333};
334
335// D923 — 3072-bit; half-width between D615 and D1231.
336#[cfg(any(feature = "d923", feature = "xx-wide"))]
337pub use core_type::{
338 D923,
339 D923s0, D923s75, D923s153, D923s230, D923s307, D923s400, D923s461, D923s462,
340 D923s500, D923s616, D923s700, D923s800, D923s860, D923s900, D923s920, D923s923, D923s924,
341};
342#[cfg(any(feature = "d923", feature = "xx-wide"))]
343pub use wide_int::{Int3072, Int6144, Int12288, Uint3072, Uint6144, Uint12288};
344
345// D1231 — 4096-bit; widest tier shipped.
346#[cfg(any(feature = "d1231", feature = "xx-wide"))]
347pub use core_type::{
348 D1231,
349 D1231s0, D1231s75, D1231s153, D1231s230, D1231s307, D1231s461, D1231s616,
350 D1231s700, D1231s800, D1231s900, D1231s924, D1231s1000, D1231s1100,
351 D1231s1180, D1231s1220, D1231s1230, D1231s1231, D1231s1232,
352};
353#[cfg(any(feature = "d1231", feature = "xx-wide"))]
354pub use wide_int::{Int8192, Int16384, Uint8192, Uint16384};
355
356// ─── Construction macros (re-exports + per-scale wrappers) ────────────
357
358/// The narrow-tier proc-macros are always available with the
359/// `macros` feature; the wide-tier proc-macros are additionally
360/// feature-gated to match their target type's availability.
361#[cfg(feature = "macros")]
362pub use decimal_scaled_macros::{d9, d18, d38};
363
364#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))]
365pub use decimal_scaled_macros::d76;
366
367#[cfg(all(feature = "macros", any(feature = "d153", feature = "wide")))]
368pub use decimal_scaled_macros::d153;
369
370#[cfg(all(feature = "macros", any(feature = "d307", feature = "wide", feature = "x-wide")))]
371pub use decimal_scaled_macros::d307;
372
373// Per-scale wrappers — curated subset of pre-baked
374// `<dN>s<SCALE>!` macros that forward to the corresponding
375// proc-macro with `scale N` added. Long-tail scales remain
376// reachable via the explicit `, scale N` qualifier.
377//
378// Each alias is a tiny `macro_rules!`. We don't generate them
379// through a nested macro because `macro_rules!` doesn't support
380// directly emitting another `macro_rules!` without `$$` escapes
381// that aren't available in stable Rust; explicit per-line
382// declarations keep things debuggable and only cost ~40 lines.
383
384// D9 curated scales.
385/// `d9s0!(value)` — equivalent to `d9!(value, scale 0)`.
386#[cfg(feature = "macros")] #[macro_export]
387macro_rules! d9s0 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d9!($v, scale 0 $(, $($rest)*)?) }; }
388/// `d9s2!(value)` — equivalent to `d9!(value, scale 2)`.
389#[cfg(feature = "macros")] #[macro_export]
390macro_rules! d9s2 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d9!($v, scale 2 $(, $($rest)*)?) }; }
391/// `d9s4!(value)` — equivalent to `d9!(value, scale 4)`.
392#[cfg(feature = "macros")] #[macro_export]
393macro_rules! d9s4 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d9!($v, scale 4 $(, $($rest)*)?) }; }
394/// `d9s6!(value)` — equivalent to `d9!(value, scale 6)`.
395#[cfg(feature = "macros")] #[macro_export]
396macro_rules! d9s6 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d9!($v, scale 6 $(, $($rest)*)?) }; }
397/// `d9s9!(value)` — equivalent to `d9!(value, scale 9)`.
398#[cfg(feature = "macros")] #[macro_export]
399macro_rules! d9s9 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d9!($v, scale 9 $(, $($rest)*)?) }; }
400
401// D18 curated scales.
402/// `d18s0!(value)` — equivalent to `d18!(value, scale 0)`.
403#[cfg(feature = "macros")] #[macro_export]
404macro_rules! d18s0 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d18!($v, scale 0 $(, $($rest)*)?) }; }
405/// `d18s2!(value)` — equivalent to `d18!(value, scale 2)`.
406#[cfg(feature = "macros")] #[macro_export]
407macro_rules! d18s2 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d18!($v, scale 2 $(, $($rest)*)?) }; }
408/// `d18s4!(value)` — equivalent to `d18!(value, scale 4)`.
409#[cfg(feature = "macros")] #[macro_export]
410macro_rules! d18s4 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d18!($v, scale 4 $(, $($rest)*)?) }; }
411/// `d18s6!(value)` — equivalent to `d18!(value, scale 6)`.
412#[cfg(feature = "macros")] #[macro_export]
413macro_rules! d18s6 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d18!($v, scale 6 $(, $($rest)*)?) }; }
414/// `d18s9!(value)` — equivalent to `d18!(value, scale 9)`.
415#[cfg(feature = "macros")] #[macro_export]
416macro_rules! d18s9 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d18!($v, scale 9 $(, $($rest)*)?) }; }
417/// `d18s12!(value)` — equivalent to `d18!(value, scale 12)`.
418#[cfg(feature = "macros")] #[macro_export]
419macro_rules! d18s12 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d18!($v, scale 12 $(, $($rest)*)?) }; }
420/// `d18s18!(value)` — equivalent to `d18!(value, scale 18)`.
421#[cfg(feature = "macros")] #[macro_export]
422macro_rules! d18s18 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d18!($v, scale 18 $(, $($rest)*)?) }; }
423
424// D38 curated scales.
425/// `d38s0!(value)` — equivalent to `d38!(value, scale 0)`.
426#[cfg(feature = "macros")] #[macro_export]
427macro_rules! d38s0 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 0 $(, $($rest)*)?) }; }
428/// `d38s2!(value)` — equivalent to `d38!(value, scale 2)`.
429#[cfg(feature = "macros")] #[macro_export]
430macro_rules! d38s2 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 2 $(, $($rest)*)?) }; }
431/// `d38s4!(value)` — equivalent to `d38!(value, scale 4)`.
432#[cfg(feature = "macros")] #[macro_export]
433macro_rules! d38s4 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 4 $(, $($rest)*)?) }; }
434/// `d38s6!(value)` — equivalent to `d38!(value, scale 6)`.
435#[cfg(feature = "macros")] #[macro_export]
436macro_rules! d38s6 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 6 $(, $($rest)*)?) }; }
437/// `d38s8!(value)` — equivalent to `d38!(value, scale 8)`.
438#[cfg(feature = "macros")] #[macro_export]
439macro_rules! d38s8 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 8 $(, $($rest)*)?) }; }
440/// `d38s9!(value)` — equivalent to `d38!(value, scale 9)`.
441#[cfg(feature = "macros")] #[macro_export]
442macro_rules! d38s9 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 9 $(, $($rest)*)?) }; }
443/// `d38s12!(value)` — equivalent to `d38!(value, scale 12)`.
444#[cfg(feature = "macros")] #[macro_export]
445macro_rules! d38s12 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 12 $(, $($rest)*)?) }; }
446/// `d38s15!(value)` — equivalent to `d38!(value, scale 15)`.
447#[cfg(feature = "macros")] #[macro_export]
448macro_rules! d38s15 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 15 $(, $($rest)*)?) }; }
449/// `d38s18!(value)` — equivalent to `d38!(value, scale 18)`.
450#[cfg(feature = "macros")] #[macro_export]
451macro_rules! d38s18 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 18 $(, $($rest)*)?) }; }
452/// `d38s24!(value)` — equivalent to `d38!(value, scale 24)`.
453#[cfg(feature = "macros")] #[macro_export]
454macro_rules! d38s24 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 24 $(, $($rest)*)?) }; }
455/// `d38s35!(value)` — equivalent to `d38!(value, scale 35)`.
456#[cfg(feature = "macros")] #[macro_export]
457macro_rules! d38s35 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 35 $(, $($rest)*)?) }; }
458/// `d38s38!(value)` — equivalent to `d38!(value, scale 38)`.
459#[cfg(feature = "macros")] #[macro_export]
460macro_rules! d38s38 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 38 $(, $($rest)*)?) }; }
461
462// D76 curated scales.
463/// `d76s0!(value)` — equivalent to `d76!(value, scale 0)`.
464#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))] #[macro_export]
465macro_rules! d76s0 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d76!($v, scale 0 $(, $($rest)*)?) }; }
466/// `d76s2!(value)` — equivalent to `d76!(value, scale 2)`.
467#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))] #[macro_export]
468macro_rules! d76s2 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d76!($v, scale 2 $(, $($rest)*)?) }; }
469/// `d76s6!(value)` — equivalent to `d76!(value, scale 6)`.
470#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))] #[macro_export]
471macro_rules! d76s6 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d76!($v, scale 6 $(, $($rest)*)?) }; }
472/// `d76s12!(value)` — equivalent to `d76!(value, scale 12)`.
473#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))] #[macro_export]
474macro_rules! d76s12 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d76!($v, scale 12 $(, $($rest)*)?) }; }
475/// `d76s18!(value)` — equivalent to `d76!(value, scale 18)`.
476#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))] #[macro_export]
477macro_rules! d76s18 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d76!($v, scale 18 $(, $($rest)*)?) }; }
478/// `d76s35!(value)` — equivalent to `d76!(value, scale 35)`.
479#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))] #[macro_export]
480macro_rules! d76s35 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d76!($v, scale 35 $(, $($rest)*)?) }; }
481/// `d76s50!(value)` — equivalent to `d76!(value, scale 50)`.
482#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))] #[macro_export]
483macro_rules! d76s50 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d76!($v, scale 50 $(, $($rest)*)?) }; }
484/// `d76s76!(value)` — equivalent to `d76!(value, scale 76)`.
485#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))] #[macro_export]
486macro_rules! d76s76 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d76!($v, scale 76 $(, $($rest)*)?) }; }
487
488// D153 curated scales.
489/// `d153s0!(value)` — equivalent to `d153!(value, scale 0)`.
490#[cfg(all(feature = "macros", any(feature = "d153", feature = "wide")))] #[macro_export]
491macro_rules! d153s0 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d153!($v, scale 0 $(, $($rest)*)?) }; }
492/// `d153s35!(value)` — equivalent to `d153!(value, scale 35)`.
493#[cfg(all(feature = "macros", any(feature = "d153", feature = "wide")))] #[macro_export]
494macro_rules! d153s35 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d153!($v, scale 35 $(, $($rest)*)?) }; }
495/// `d153s75!(value)` — equivalent to `d153!(value, scale 75)`.
496#[cfg(all(feature = "macros", any(feature = "d153", feature = "wide")))] #[macro_export]
497macro_rules! d153s75 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d153!($v, scale 75 $(, $($rest)*)?) }; }
498/// `d153s150!(value)` — equivalent to `d153!(value, scale 150)`.
499#[cfg(all(feature = "macros", any(feature = "d153", feature = "wide")))] #[macro_export]
500macro_rules! d153s150 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d153!($v, scale 150 $(, $($rest)*)?) }; }
501/// `d153s153!(value)` — equivalent to `d153!(value, scale 153)`.
502#[cfg(all(feature = "macros", any(feature = "d153", feature = "wide")))] #[macro_export]
503macro_rules! d153s153 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d153!($v, scale 153 $(, $($rest)*)?) }; }
504
505// D307 curated scales.
506/// `d307s0!(value)` — equivalent to `d307!(value, scale 0)`.
507#[cfg(all(feature = "macros", any(feature = "d307", feature = "wide", feature = "x-wide")))] #[macro_export]
508macro_rules! d307s0 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d307!($v, scale 0 $(, $($rest)*)?) }; }
509/// `d307s35!(value)` — equivalent to `d307!(value, scale 35)`.
510#[cfg(all(feature = "macros", any(feature = "d307", feature = "wide", feature = "x-wide")))] #[macro_export]
511macro_rules! d307s35 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d307!($v, scale 35 $(, $($rest)*)?) }; }
512/// `d307s150!(value)` — equivalent to `d307!(value, scale 150)`.
513#[cfg(all(feature = "macros", any(feature = "d307", feature = "wide", feature = "x-wide")))] #[macro_export]
514macro_rules! d307s150 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d307!($v, scale 150 $(, $($rest)*)?) }; }
515/// `d307s300!(value)` — equivalent to `d307!(value, scale 300)`.
516#[cfg(all(feature = "macros", any(feature = "d307", feature = "wide", feature = "x-wide")))] #[macro_export]
517macro_rules! d307s300 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d307!($v, scale 300 $(, $($rest)*)?) }; }
518/// `d307s307!(value)` — equivalent to `d307!(value, scale 307)`.
519#[cfg(all(feature = "macros", any(feature = "d307", feature = "wide", feature = "x-wide")))] #[macro_export]
520macro_rules! d307s307 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d307!($v, scale 307 $(, $($rest)*)?) }; }