decimal-scaled
Docs • Benchmarks • Algorithms • Roadmap • API reference
Const-generic base-10 fixed-point decimals for Rust — bit-exact,
≤ 0.5 ULP correctly-rounded integer-only transcendentals,
deterministic on every platform, no_std-friendly.
Install
[]
= { = "0.4", = ["macros"] }
First use
use ;
// The most common way to make a value: a literal, scale inferred from
// its digits.
let x = d38!; // D38<6>
assert_eq!;
let price: D38s12 = "19.99".parse.unwrap;
let qty = from_int;
let total = price * qty; // 59.97 exactly
assert_eq!;
assert_eq!;
// Transcendentals are correctly rounded to ≤ 0.5 ULP, integer-only,
// and bit-identical across platforms.
let sqrt2 = d38!.sqrt_strict;
What it does
Every value is raw × 10^(-SCALE) for a compile-time SCALE. There is
exactly one bit pattern per logical value — no normalisation, no
per-value scale byte, no heap allocation. 0.1 + 0.2 == 0.3 is true,
and so is hash(1.10) == hash(1.1) at the same scale.
Thirteen storage widths from D9 (32-bit, ~9 decimal digits) to
D1232 (4096-bit, ~1232 decimal digits) share an identical API.
Pick the narrowest width that covers your range.
The two guarantees nothing else on crates.io currently combines:
- ≤ 0.5 ULP correctness on every transcendental —
ln/exp/sin/cos/tan/sqrt/cbrt/powf/asin/acos/atan/atan2/sinh/cosh/tanh/asinh/acosh/atanh/to_degrees/to_radianslands within half an ULP of the mathematically exact result, and the bit pattern is identical on every machine. The defaultstrictpath is integer-only andno_std-compatible. - Caller-chosen rounding mode at every lossy operation. The
default is
HalfToEven(IEEE 754 default). Every lossy entry point (*///%, therescalefamily, every strict transcendental) ships a*_with(mode)sibling that takes aRoundingMode:HalfToEven·HalfAwayFromZero·HalfTowardZero·Ceiling·Floor·Trunc. The crate-wide default is also selectable at compile time via therounding-*Cargo features.
Correctly rounded — and the only crate that is
Worst-case error of each transcendental, measured against a high-precision oracle (worst result across every tested input). Each cell shows the LSBε — least significant bits in error, the count of low-order bits of the stored value that are wrong — with the worst ULP distance from the true value in parentheses:
| Function | decimal-scaled | g_math | fastnum | rust_decimal | dashu-float | decimal-rs |
|---|---|---|---|---|---|---|
| exp | ✓ 0 (0.50) | ✗ 65 (2.3e19) | ✓ 0 (1e-16) | ✓ 0 (2.7e-6) | ✓ 0 (1e-16) | ✓ 0 (1.3e-15) |
| ln | ✓ 0 (0.50) | ✗ 6 (49.5) | ✓ 0 (1e-16) | ✗ 31 (1.1e9) | ✗ 1 (1.00) | ✓ 0 (1e-16) |
| sin | ✓ 0 (0.50) | ✗ 64 (1.7e19) | ✓ 0 (1.7e-12) | ✓ 0 (2.1e-9) | — | — |
| cos | ✓ 0 (0.50) | ✗ 6 (50.0) | ✓ 0 (5.2e-12) | ✓ 0 (2.4e-9) | — | — |
| tan | ✓ 0 (0.50) | ✗ 65 (2.1e19) | ✓ 0 (1.4e-12) | ✗ 36 (4.3e10) | — | — |
| atan | ✓ 0 (0.50) | ✗ 64 (1.6e19) | ✓ 0 (1e-16) | — | — | — |
| sqrt | ✓ 0 (0.50) | ✗ 6 (49.6) | ✓ 0 (1e-16) | ✓ 0 (7.2e-7) | ✓ 0 (1e-16) | ✓ 0 (3e-16) |
| cbrt | ✓ 0 (0.50) | — | ✓ 0 (1e-16) | — | — | — |
| rounding | all 6, caller-chosen | nearest | HalfUp | HalfEven | HalfAway | unspec. |
✓ = 0 LSBε (correctly rounded — the stored value is exactly right, ≤ 0.5 ULP from true) on every tested input. ✗ = at least one input with ≥ 1 LSBε. — = not implemented by that crate. First number = worst-case LSBε (least significant bits in error); parenthesised = worst ULP distance from the true value.
decimal-scaled is the only crate ✓ on every function — and its ✓ holds
for all six rounding modes and all thirteen widths (D9 … D1232).
At deep precision the field collapses. Repeat the test at a 150-digit
scale and only decimal-scaled computes all eight functions
correctly; the fixed-precision crates (g_math ≈ 19, rust_decimal ≈ 28,
fastnum ≈ 34, decimal-rs ≈ 38 digits) can no longer represent the value
at all, and arbitrary-precision dashu-float — the lone peer that
reaches it — still misses ln.
Scope of this table: measured at a 19-digit scale (D38<19>) and a
150-digit scale (D307<150>, with D616<308> also verified), under
HalfToEven; each cell is the worst case across the scales a library
supports. It samples a slice of the full matrix — decimal-scaled's
guarantee holds across all six rounding modes and all thirteen
widths (D9 … D1232). For the complete per-scale, per-width,
per-mode tables and the methodology, see the
benchmarks.
Speed across the width range
Per-operation throughput against the wider numeric ecosystem (bnum,
ruint, rust_decimal, fixed), at two representative widths:
i128 / D38 — 128-bit, scale 19:

i1024 / D307 — 1024-bit, scale 150:

Full per-width charts (32-bit … 4096-bit) and the methodology are in the benchmarks.
Documentation
In-depth guides live under docs/:
- Getting started — constructing values, arithmetic, formatting, parsing.
- The width family —
D9throughD1232, scale aliases, theDecimaltrait, picking a tier. - Conversions — integers, floats, cross-width widening / narrowing, the float bridge.
- Cross-scale operations —
mul_of/add_of/cmp_of/clamp_of/ etc. on every width for mixed-width mixed-SCALE expressions, plus the nightly-gatedcross::mul(a, b)auto-inferred form. - Rounding —
RoundingMode, the_withpairs,rescale, the compile-timerounding-*features. - Strict mode — integer-only
*_stricttranscendentals, the ≤ 0.5 ULP guarantee. - The
d*!macros — compile-time decimal literals, scale inference, scientific / radix notation. - Cargo features — every feature flag with what it enables and the common configurations.
- Benchmarks — head-to-head against
bnum,ruint,rust_decimal,fixed,fastnum,dashu-float,bigdecimal, and the fast-vs-strict trade. - Algorithms — every kernel with its citation (Möller–Granlund, Brent, Knuth, Karatsuba, Burnikel–Ziegler, Mercator / Cody–Waite, …).
- Roadmap — what's queued (signed
SCALE, RNG surface, wide-tier perf catch-up, downstream adapter / ecosystem crates). - Changelog — release-by-release notes.
- Contributing — algorithm-library tour, adding a per-(width, scale) override, performance gates, license-compatibility rules.
API reference: https://docs.rs/decimal-scaled/.
License
Licensed under either of:
- MIT license (LICENSES/MIT.md)
- Apache License, Version 2.0 (LICENSES/Apache-2.0.md)
at your option.
Copyright 2026 John Moxley. Third-party code attributions are listed in LICENSE-THIRD-PARTY.