Skip to content

Cargo features

[dependencies]
decimal-scaled = { version = "0.5", default-features = false, features = ["alloc"] }

Core features

Feature Default Enables
std yes The f64-bridge transcendentals (trig, log/exp, sqrt, …) and from_f64 constructors. Pulls in alloc.
alloc yes Display::to_string and FromStr on no_std. Required — targets without alloc are not supported.
serde yes Serialize / Deserialize via serde_helpers — raw-storage string wire format (see Serde).
strict yes Marks the build as on the strict path: plain sqrt / ln / etc. dispatch to the integer-only ≤ 0.5 ULP *_strict methods. no_std-friendly. Strict is also the dispatch when no feature is set at all — this feature mainly signals intent and survives a transitive fast flip from a downstream crate (which still resolves to strict). See strict mode.
macros no The d38! / d76! / etc. compile-time literal macros. See the macro guide.
fast no Opt out of strict dispatch: plain methods forward to the f64 bridge for speed at the cost of platform-libm-dependent ≈ 16-digit precision. Only takes effect when strict is NOT enabled. Three-step opt-out: default-features = false + add fast + std + DON'T re-add strict. Both *_strict and *_fast named methods stay available regardless.
dyn no Object-safe DynDecimal trait + DecimalWidth / RawStorage enums for runtime-polymorphic decimal handles. Enables alloc (the boxed returns are the façade's sanctioned heap path). Ships impls for D18 / D38. See Runtime polymorphism.

Notes:

  • The *_strict and *_fast named methods (sqrt_strict, ln_strict, sqrt_fast, …) are always compiled, regardless of any feature flag — no feature removes them (the only gate is that *_fast needs std for the f64 bridge). The strict / fast features only choose what the plain methods (sqrt, ln, …) resolve to.
  • With strict on, the plain transcendentals use the integer path even if std is also enabled.
  • The strict methods are held to the IEEE 754 correctly-rounded standard (within 0.5 ULP of the exact result). See strict mode for the dual-API rules and the current per-function implementation status.

Default-rounding-mode features

Exactly one of these may be enabled to change the crate-wide default RoundingMode at compile time (default is HalfToEven). See the rounding guide.

Feature Default mode
rounding-half-away-from-zero HalfAwayFromZero
rounding-half-toward-zero HalfTowardZero
rounding-trunc Trunc
rounding-floor Floor
rounding-ceiling Ceiling

Wide-tier features

The wide decimal types are backed by an in-tree const-generic big integer, Int<N> over N u64 limbs (internal to the crate); there is no external big-integer dependency. They are opt-in per width, with three umbrella features covering increasing precision ranges.

Feature Enables
d57 D57 (192-bit storage, MAX_SCALE = 56) — half-width between D38 and D76
d76 D76 (256-bit storage, MAX_SCALE = 75)
d115 D115 (384-bit storage, MAX_SCALE = 114) — half-width between D76 and D153
d153 D153 (512-bit storage, MAX_SCALE = 152)
d230 D230 (768-bit storage, MAX_SCALE = 229) — half-width between D153 and D307
d307 D307 (1024-bit storage, MAX_SCALE = 306)
d462 D462 (1536-bit storage, MAX_SCALE = 461) — half-width between D307 and D616
d616 D616 (2048-bit storage, MAX_SCALE = 615)
d924 D924 (3072-bit storage, MAX_SCALE = 923) — half-width between D616 and D1232
d1232 D1232 (4096-bit storage, MAX_SCALE = 1231)
wide umbrella: enables D57 / D76 / D115 / D153 / D230 / D307
x-wide umbrella: adds D462 / D616 on top of wide
xx-wide umbrella: adds D924 / D1232 on top of x-wide

The half-width tiers exist so you only pay storage cost for the precision you actually need — if your accumulator needs ~60 digits, D57 saves a third of the bytes per value vs D76, and the same across every wider tier pair. Every adjacent pair in the ladder has From / TryFrom impls plus .widen() / .narrow() helpers.

Nightly-only

Feature Enables
experimental-floats f16 / f128 entry points on the float bridge (from_f16, to_f128, …). Requires a nightly toolchain.
cross-scale-ops Auto-inferred cross-scale free functions under decimal_scaled::cross (cross::mul(a, b) etc.) via generic_const_exprs. Requires a nightly toolchain. The stable D{N}<S>::mul_of(a, b) explicit-target form is available without this feature.

Common configurations

# Default — std, serde, and the integer-only ≤ 0.5 ULP `*_strict`
# transcendentals dispatched by plain `sin` / `ln` / `sqrt`.
decimal-scaled = "0.5"

# `no_std`, still with serde and the deterministic strict path.
decimal-scaled = { version = "0.5", default-features = false,
                   features = ["serde", "alloc", "strict"] }

# Add the half-width and wider tiers (D57–D307).
decimal-scaled = { version = "0.5", features = ["wide", "macros"] }

# Add the extra-wide tiers (D462 / D616) on top of wide.
decimal-scaled = { version = "0.5", features = ["x-wide", "macros"] }

# Add the xx-wide tiers (D924 / D1232) — research-grade precision.
decimal-scaled = { version = "0.5", features = ["xx-wide", "macros"] }

# Bank-statement rounding: HalfAwayFromZero as the crate-wide default.
decimal-scaled = { version = "0.5",
                   features = ["wide", "rounding-half-away-from-zero"] }

# Speed over determinism — plain transcendentals dispatch to the f64
# bridge (~16 decimal digits of platform-libm precision). The
# `*_strict` named methods remain available for the parts of your
# code that need them.
decimal-scaled = { version = "0.5", default-features = false,
                   features = ["std", "fast"] }

Runtime polymorphism

The typed Decimal trait is monomorphised: every Dxx<S> is a distinct compile-time type and generic code over T: Decimal pays no runtime cost. That model breaks down when the width or scale is chosen at runtime — config-driven types, plugin interfaces, a Vec<Box<…>> of mixed decimals.

The dyn feature adds a deliberately small, object-safe trait DynDecimal for exactly that case:

```rust,ignore use decimal_scaled::{D38, DynDecimal, DecimalWidth};

let values: Vec> = vec![ Box::new(D38::<2>::try_from(150i32).unwrap()), // 150.00 Box::new(D38::<5>::try_from(2i32).unwrap()), // 2.00000 ]; let sum = values[0].add(&*values[1]).unwrap(); assert_eq!(sum.width(), DecimalWidth::D38); assert_eq!(sum.scale_dyn(), 5); // auto-rescale to wider scale ```

Semantics:

  • Binary ops on different widths return None. No implicit widening across storage tiers.
  • Binary ops on the same width but different scales losslessly rescale both sides to the wider scale and return the result at that scale.
  • Overflow at any step returns None instead of panicking.
  • Downcast back to the typed surface via DynDecimal::as_any().downcast_ref::<Dxx<S>>() once you know the concrete type.

Scope: the dyn feature ships impls for D18 and D38 only. Wider tiers would require enumerating up to 1232 scale instantiations per binary op per width and serve compute-bound code where the boxing cost of dyn is wrong anyway. The DecimalWidth / RawStorage enums carry variants for every shipped width so the API is forward-compatible if those impls land later.

Cost: each binary op heap-allocates one Box<dyn DynDecimal> (plus intermediate boxes when auto-rescale is needed). Use the typed Decimal surface in hot paths.