Changelog¶
All notable changes to decimal-scaled are documented here.
The format is loosely based on Keep a Changelog, and this project follows Semantic Versioning.
[0.5.0] — 2026-06-14¶
An architecture release. Every decimal width is now backed by the
const-generic Int<N> / Uint<N> integer layer introduced in this
cycle, unifying storage across the full tier table and eliminating the
native (primitive-integer) backend entirely. The 32-bit tier D9 is
removed. The integer layer itself gains a parallel types / policy /
algos / limbs layout mirroring the decimal layer, plus a suite of
performance and correctness fixes that fed directly into the decimal
migration. The crate-wide default rounding mode features, docs
single-source tooling, precision harness, and pre-merge CI gates all
land alongside.
Removed¶
D9(32-bit tier) removed in full. TheD9<SCALE>type, thed9!construction macro (bothconstandfnforms), thewtag::D9tag constant, thedynregistry entry, and all corresponding documentation and prose are gone.D18is now the narrowest supported width. Callers usingD9should migrate toD18; the operator surface, rounding, and conversion APIs are identical.- Native decimal backend removed. The non-wide macro arms that
dispatched arithmetic, display, sign, conversions, equalities, and
num_traitsthrough primitive-integer storage are deleted. Every decimal tier now routes through the unifiedInt<N>wide arms. The deadround_with_mode_nativehelper and thei64-widening arithmetic machinery (@native_i64_wider,i64dispatch arm) that served onlyD9are gone. Unsignedtrait removed. The internal marker trait carried no live impls after the native backend was dropped.- CodSpeed perf-tracking removed. The
codspeed.ymlworkflow and thecodspeed-criterion-compatbench alias are gone; the benches now build directly against the upstreamcriterioncrate, and perf is tracked through thebench-branch-compareworkflow.
Changed¶
D18storage changed fromi64toInt<1>. The 64-bit tier now stores its scaled integer inInt<1>(a one-limb const-generic integer) instead of a barei64.to_bitsandfrom_bitsnow accept and returnInt<1>where they previously usedi64. This is a breaking type change for call sites that stored or pattern-matched the raw bits directly.D38storage changed fromi128toInt<2>. The 128-bit tier is now backed byInt<2>(two 64-bit limbs).to_bitsandfrom_bitsnow useInt<2>instead ofi128. Existing call sites that bridged throughi128should construct viaInt::<2>::from(v)/Int::<2>::try_from(v)?and read back withi128::from(int2)(theFrom/TryFromtrait surface). This is a breaking type change.D18narrowing conversions tightened.TryFrom<i128>andTryFrom<u128>forD18now reject values outside thei64storage range (previously the wide arms assumed ≥ 128-bit storage and silently truncated).FromPrimitive::from_u64on narrowInt<1>storage likewise rejects out-of-range inputs.checked_div/wrapping_div/overflowing_div/saturating_divround to nearest like the/operator. These four methods previously performed a truncating integer divide of the scaled numerator, disagreeing with the rounding/operator (e.g.20/3at SCALE 2 returned666instead of667). They now route through the sameround_with_mode_widestep that/uses. This fixes a long-standing divergence on the wide tiers.Int<N>/Uint<N>are now the public storage types. All shipped widths (D18 through D1232) exposeInt<N>as theirStoragetype.Int<N>/Uint<N>are re-exported from the crate root, anddecl_wide_int!is removed.- BREAKING: the named-width integer aliases are removed. The
crate-root
decimal_scaled::{Int64, Int128, Int192, …, Int16384}(and the matchingUint*) re-exports are gone — name the storage as the const-genericInt<N>/Uint<N>instead (e.g.Int<4>for the formerInt256). - BREAKING: integer → decimal construction is now
TryFrom, notFrom. Every primitive-integer source (i8…i64,u8…u64,i128,u128) builds a decimal viaTryFrom, returningConvertError::Overflowwhenvalue * 10^SCALEexceeds the storage:let d = D38::<2>::try_from(20i64)?;. Scaling can overflow even for small inputs near a width's top scale, so the old infallibleFrom<iN>impls (which silently wrapped on overflow) are removed, along with theDecimalConvert::from_i32trait method and the inherentfrom_int/from_i32constructors. - Uniform overflow & domain contract — strict ops panic by
default. A strict transcendental panics on a domain error
(
lnof a non-positive value,asinoutside[-1, 1], …) or an out-of-range correctly-rounded result, in both debug and release, independent of tier and scale — the condition is detected once and a wrapper applies the policy. The newchecked_*siblings (see Added) opt intoOptionreturns instead of a panic. - Published crate slimmed to a source allowlist. The package now
ships via a
[package] includeallowlist (src/**plus the README / CHANGELOG / CONTRIBUTORS / LICENSE files) rather than the whole repository, excluding the benchmarks, golden harness, dev docs, and tooling from the published.crate.
Fixed¶
saturating_divwith a zero divisor now panics (matchingstd). Previously it saturated toMAX, silently returning a nonsense value where every other integer type in Rust panics in debug.checked_rem/overflowing_remhonourMIN % -1overflow.checked_rem(MIN, -1)now returnsNone;overflowing_rem(MIN, -1)returns(ZERO, true), matching the primitive integer contract. Both tests that covered this case were un-ignored.dynfeature compiles after theInt<N>migration. Thedyn_bridgemacro previously assumed primitive storage (10 as $Storage); it now constructs the rescale multiplier viaInt::from_i128(10).pow(shift)and converts to theRawStoragevariant primitive withas_i128().D18fits thei64variant,D38fits thei128variant exactly.Int<N>arithmetic sign-preserving right-shift restored.Shrwas not sign-extending correctly for negative values, corrupting wide-tier transcendental range-reduction. Fixed; confirmed against the 264-cell golden suite.Int<N>::leading_zerosis two's-complement-faithful. Negative values now correctly return0(all bits set under negation), matchingiN::leading_zerosparity. The wideMul/Divfast-path now takes magnitude viaunsigned_absrather than callingleading_zeroson signed values.D38doc examples use the publicdecimal_scaled::Intpath. Examples that usedcrate::intfailed in downstream doctests; corrected to the external-crate path.- Intra-doc link warnings resolved under
-D warnings. Broken links surfaced by the newcargo doc --no-deps -D warningsgate are fixed.cargo docis now clean under-D warnings. D18narrowingTryFrom<u128>rejects values past signed storage range. Wide arms previously assumed ≥ 128-bit storage and truncated silently.Int<1>is the first narrow wide-storage tier; the guard is a no-op for wider tiers.- Correctly rounded across all six rounding modes on the wide
tiers. A family of directed-rounding fixes closes the last
sub-0.5-ULP gaps the six-mode golden surfaced: the tiny-argument
directed wrong-mode in the wide-tier trig /
atan2/asinhpaths, theexpdeep-underflow directed-rounding inversion, apowf(2, -200)-class deep-underflow mis-round at the D57 / D76 mid-scales, and a large-argument panic in the wide hyperbolics. Every shipped width is now0 (0)— correctly rounded — under all six modes (HalfToEven/HalfAwayFromZero/HalfTowardZero/Floor/Ceiling/Trunc).
Added¶
Int<N>/Uint<N>const-generic integer layer. A const-generic fixed-width signed / unsigned integer pair parameterised by 64-bit limb count (N) backs every decimal tier.Int<1>replacesi64;Int<2>replacesi128; wider tiers map toInt<3>throughInt<64>. The layer is mirrored undersrc/int/with types / policy / algos / limbs sub-modules matching the decimal layer's six-bucket layout.BigInttrait — a single unified trait surface forInt<N>, providing the full method coverage (LIMBS,BITS,ZERO,ONE,MAX,MIN,leading_zeros,wrapping_add,wrapping_sub,wrapping_mul,div_rem,isqrt,widen,narrow, …).Int<N>/Uint<N>std-aligned primitive conversion surface. The crate-internalfrom_i128/as_i128helpers are nowpub(crate); the public conversions go through the idiomaticFrom/TryFromtraits:From<i8>/From<i16>/From<i32>/From<i64>forInt<N>— infallible widening from a narrow signed primitive.From<u8>/From<u16>/From<u32>forInt<N>, andFrom<u8>/From<u16>/From<u32>/From<u64>forUint<N>— infallible widening from a narrow unsigned primitive.TryFrom<u64>forInt<N>(au64can overflowInt<1>),TryFrom<i128>forInt<N>, andTryFrom<u128>for bothInt<N>andUint<N>— fallible narrowing, returningConvertError::Overflowwhen the value is out of range.From<Int<1>>/From<Int<2>>fori64/i128and the matchingPartialEq/PartialOrdbridges, so the one- and two-limb tiers interoperate with the corresponding primitive.- Non-allocating stack-scratch Karatsuba multiply in
wide_int::widen_mul. The non-alloc Karatsuba dispatcher is wired intowiden_mul; the schoolbook path remains optimal at all currently-shipped widths (crossover would require limb counts beyondInt<64>), so the threshold is parked for GHA bench validation. div-by-10^19base case for wide-intto_string. Theto_stringimplementation now peels 19-digit chunks by dividing by10^19(the largest power-of-ten below2^64), emitting each chunk with native arithmetic and calling the expensive full-width divide once per 19 digits. Measured speedup: 2.3× (Int256/ D76) to 14.9× (Int4096/ D1232).mg_divideclean-room rewrite from the Möller–Granlund 2011 paper.MG_EXP_MAGICSis now generated at compile time by aconst fn(mg_reciprocal) that computes the magic constant via binary long division, following the paper's reciprocal formula. No literal table values are copied from any prior implementation. The function namesmul_u128_to_u256anddivmod_pow10_2wordreplace the prior upstream-derived identifiers.LICENSES/THIRD-PARTY.mdconfirms no third-party code is incorporated;ALGORITHMS.mdcarries the clean-room declaration and a courtesy prior-art note.no_std+allocbuild restored and CI-gated. A regression whereallocandnum_traits::Floatimports were missing from the no-std path is fixed. A dedicated CI job now builds and tests the crate under--no-default-featuresto prevent future regressions.std/no_stdabstraction layer in policy.table_cacheandfloat_seednow live insrc/policy/shims that select thestd-backed (thread-local, memoised) orno_std(recompute) implementation at compile time, instead of havingstd/no_stdconditional logic scattered across algorithm bodies.src/int/integer layer layout. Thewide_int/sub-crate is absorbed; integer code is reorganised undersrc/int/types/,src/int/policy/,src/int/algos/, andsrc/int/limbs/mirroring the decimal six-bucket layout. The formerFixedInt/WideStorage/WideInttraits are unified into the singleBigInttrait, rooted insrc/int/types/traits/.(N, SCALE)policy-matcher dispatch layer. Every transcendental family (sqrt/cbrt/exp/ln/pow/trig) routes its typedDxx<S>method shell through a per-family policy trait whose body is aconst fn select::<N, SCALE>()returning anAlgorithmenum, dispatched via an inlineconst { select::<N, SCALE>() }match. BecauseNandSCALEareconstat every monomorphisation, the selector const-folds to its single live arm — each concrete type compiles to a direct call to one kernel with zero runtime dispatch cost. The integer layer carries its own parallel matcher families (mul,div_rem) undersrc/int/policy/. This replaces the previous scattered per-methodcfg/ width-branch dispatch.- Stable cross-width-and-scale conversion:
convert_from/convert_from_with. A new method on every width converts from any other width and scale in one step (e.g.D76::<40>::convert_from(some_d38_s12)), rescaling and resizing together, with a_with(mode)sibling for the rounding choice. Complements the existing same-scaleFrom/TryFromwidening / narrowing and the cross-scale_ofoperator surface. - Precision harness unified.
tests/ulp_strict_golden.rsand the comparative precision suite share a singlePrecisionSubjectharness with committable result TSV files underresults/precision/. TheprecisionCI workflow is extended with alib-cmp-precisionself-refresh that regenerates the TSVs after a clean matrix pass. Precision tables inREADME.mdanddocs/benchmarks.mdare generated from those TSVs viarender_docs.pyto prevent prose drift. - Pre-merge CI gates.
.github/workflows/ci.ymlruns on every pull request and every push tomain/release/*, enforcing: fullcargo testunder all feature combinations (including default features),cargo doc --no-deps -D warnings(catches broken intra-doc links before merge), and an informationalcargo clippy --libstep. A separate commit-hygiene gate rejects attribution trailers in commit messages. bench-fullself-refresh. After each per-width matrix sweep, thebench-fullworkflow commits a refresheddocs/benchmarks.mdso bench data and prose stay in lockstep without a manual step.- Architecture documentation.
docs/ARCHITECTURE.mdships a Mermaid layer diagram and sequence diagram showing the integer layer's own dispatch / algorithm tiers alongside the decimal layer.src/int/folders mirrorsrc/under the diagram.mkdocs.ymlenables Mermaid rendering. RELEASING.mdand release PR checklist template. The release system of record (versioning policy, branch workflow, docs + benchmark refresh, publish steps) is codified inRELEASING.mdand a PR checklist template. Performance benchmarking is advisory (not a required gate).- Checked transcendental siblings. Every strict transcendental
(
exp/ln/log/log2/log10/sqrt/cbrt, the trig / inverse-trig / hyperbolic families,powf,hypot, …) gains a non-panickingchecked_*pair returningOption<Self>:checked_<fn>_strict(self, …)and its_with(mode)sibling.Noneis returned exactly where the default strict form would panic — a domain error or an out-of-range correctly-rounded result; an in-rangechecked_*result is bit-identical to the default form's. - Crate-default rounding-mode features. Five mutually-exclusive
rounding-*Cargo features —rounding-half-away-from-zero,rounding-half-toward-zero,rounding-trunc,rounding-floor,rounding-ceiling— select theRoundingModeused by the no-argrescale/ strict /_offamily. With none enabled the default is half-to-even (banker's rounding); every*_with(mode)method keeps an explicit-mode form regardless of the feature.
[0.4.4] — 2026-05-21¶
A correctness release. Every *_strict transcendental is now provably
correctly rounded — within 0.5 ULP, i.e. 0 LSB of error at the storage
scale — under all six rounding modes, at all thirteen widths,
across the full 22-function surface. 0.4.3 secured the primary
functions under nearest rounding; 0.4.4 extends the guarantee to the
directed rounding modes and the derived functions, with no exceptions
and no ignored cells.
Fixed¶
- Directed rounding (
Trunc/Floor/Ceiling/HalfTowardZero/HalfAwayFromZero) for the wide-tier transcendentals: the working-scale approximation could previously round one LSB the wrong way near a storage grid line. Resolved with residual-sign Ziv escalation; the nearest-mode fast path is unchanged. - Derived transcendentals
log/log2/log10/exp2/sinh/cosh/tanh/asinh/acosh/atanh/powfare now correctly rounded under every mode (they previously applied their final rounding without the directed-rounding escalation, and several carried cancellation or overflow-edge error at extreme inputs). acoshnear 1 andatanhnear ±1: removed catastrophic cancellation by reformulating throughlog1p, computing1∓xandv²−1as exact working-scale gaps.sinh/cosh/tanhat large arguments: corrected a sign-direction error that turned the relative error ofe^−|x|into a large absolute error in its reciprocal; the dominant term is now always evaluated at|x|. Wide tiers near the storage-overflow edge now compute in a wider working integer.log_b(b^k)and exact-powerpowf(e.g.powf(4, 0.5)) return the exact result under directed rounding.tanhtiny arguments: directed rounding of the compressing linear band.D38log2/log10at maximum scale: removed ani128overflow in the exact-power detection.
Changed¶
- The correct-rounding guarantee — previously documented for the primary functions under nearest rounding — now holds for the entire transcendental surface under all six rounding modes and all widths.
Internal¶
- Added correctly-rounded integer
log1p/expm1kernels (internal; a public API is planned). - Expanded the strict-golden suite to the full 22-function × 13-width ×
6-mode matrix, asserting
delta == 0against an external mpmath oracle (286 cells, none ignored).
[0.4.3] — 2026-05-20¶
A correctness-and-perf release. Closed every precision hole found by
a new external-oracle test suite, fixed two latent wide-tier overflow
bugs, and improved wide-tier mul while adding a cross-scale API.
Added¶
- Cross-scale
_ofoperator surface on all 13 widths (stable):mul_of/add_of/sub_of/div_of/rem_of/max_of/min_of/clamp_of(+_with(mode)siblings) and comparatorscmp_of/eq_of/lt_of/ … accepting any-narrower-width any-SCALE operands via the new publicWidthLE<Target>trait, plus cross-widthPartialEq/PartialOrdat equal SCALE. A nightly-gatedcross-scale-opsfeature adds auto-inferredcross::{mul,add,sub,div,rem,…}free functions. - mpmath external-oracle precision suite —
tests/ulp_strict_golden.rs(golden tables generated atmp.dps ≥ 2·SCALE + 64),tests/ulp_proptest.rs(14 hard-input category strategies), and a 7361-case hard-input corpus covering half-ULP ties, catastrophic cancellation, range-reduction breakpoints, near-pole conditioning, and inverse-identity round-trips. Wired into theprecisionCI gate. - powf integer-exponent fast path (
|n| ≤ 64) viapowi, bit-exact and ~107× at D38<19> / ~27× at D307<150> for integer exponents. - Tang
lndeep-band lookup slots extending the ladder to D230<115>, D307<290>, D616<590>, D924<900>, D1232<1200>. - Const
POW10_TABLEfor the D38–D616 tiers (get_uncheckedafter a bounds guard); ~22% on D76<35>ln, ~19% onsin. - Newton–Raphson reciprocal divide for the
÷ 10^SCALEstep, dispatched at the bench-validated cells (Int2048 ≥ s200, Int3072 ≥ s200, Int4096 ≥ s400). Cite a textbook reciprocal iteration; MG magic-multiply remains canonical elsewhere. - AGM precision lift —
ln_strict_agm/exp_strict_agmnow hold 0.5 ULP at every wide tier (per-call working-scale guard sized to absorb thesqrt/2^kamplification). The dispatcher keepsln_strict/exp_stricton the artanh / Tang path; lifted AGM stays the alternate path (it loses to artanh / Tang at every shipped tier × SCALE measured). bench-allworkflow — one-click orchestrator dispatching the full release sweep (per-widthfull_matrix+lib_cmp+ cross-versionbench-history).
Fixed¶
- D153 / D1232
expprecision at large|v|— the narrow-GUARD Tangexpkernel reassembled2^k · exp(s)at a fixed working scale, amplifying thek · ln 2reduction residual by up to2^k.tang_exp_fixednow lifts the working scale by⌈|k|·log₁₀2⌉ + guardand narrows back, restoring 0.5 ULP. - D1232 transcendental-constant truncation — the u128 magnitude
buffer in
mg_divide::div_wide_pow10{,_chain}_withwas sized for Int8192 (64 u128 limbs) and silently truncated the Int16384 work integer, corruptingpi/ln 2and every constant at D1232 working scales ≳ 620. Buffer now sized to the work integer'sWideInt::U128_LIMBS. - D115 high-scale transcendental overflow — D115's transcendental
work integer was
Int1024, too small for thet·t ≈ 1040-bitintermediate inln 2evaluation at SCALE 114;exp_strict/ln_strictpanicked. Promoted D115's work integer toInt2048. tannear-pole precision (d153 / d307) —tan = sin/cosamplified the working-scale rounding by1/cos ≈ |tan|near odd multiples of π/2. The lookup sincos kernels now lift the working scale by⌈log₁₀|tan|⌉ + guardproportional to pole proximity.
Changed¶
- Width-adaptive
mg_dividebuffer restores and improves widemul(D76 mul ~52 ns, D307 mul ~122 ns at the history SCALEs — faster than v0.4.2), by sizing the÷ 10^SCALEstack buffer to the work integer instead of a flat 128-limb array. - Parity tolerances between Tang lookup and
wide_kernelpaths tightened to ≤ 1 storage LSB at every audited cell. ALGORITHMS.mddocuments the Tang table-driven kernels and an "evaluated and not used" section (Comba, CORDIC, Johansson denominator-collection, libmpfr port, Newton reciprocal trade-offs);ROADMAP.mdandCONTRIBUTORS.mdrefreshed;docs/benchmarks.mdand all figures regenerated from the v0.4.3 sweep.
[0.4.2] — 2026-05-19¶
A perf release. Every wide tier shipped a bespoke narrow-GUARD Tang
lookup slot for ln at its popular mid-storage SCALE, plus shared
kernel and infrastructure wins. Headline measured speedups at the
Tang ln slot (relative to v0.4.0 at the same cell): D115<57>
12.7×, D153<76> 19.4×, D307<150> 27×, D462<230> 27.6×, D616<308>
26.1×, D924<461> 30.8×, D1232<616> 33.8×.
Added¶
- Tang
lnnarrow-GUARD lookup slots at every wide tier: D57<20>, D115<57>, D153<76>, D307<150>, D462<230>, D616<308>, D924<460>, D1232<615>. Each is M=128 with GUARD_NARROW=8 (10 for atan/inverse). Cite Tang 1989 / 1990 (ACM TOMS 16(4)). - Tang
expsurface lookup at D57<18..=22>, D115<57>, and D153<70..=82>. At D307 and wider the surface-Tangexploses to the canonical adaptive Smith r/2^nwide_kernel::exp_fixed(the Tang post-reduction Taylor needs more wide mults than the Smith squaring tail at Int3072+); the Tangexplookup modules ship at D307 / D462 / D616 / D1232 but are not wired inpolicy::exp. Thetang_exp_fixedhelper is consumed by the hyperbolic kernels (sinh / cosh / tanh) at every tier where the trig lookup exists, in combination with the reciprocal-divide identity for an additional 1.2–1.31×. - Narrow-GUARD trig family (sin / cos / tan / atan / sinh / cosh / tanh) per tier; 1.09–1.83× speedups.
- MG-chain bit-exact half-to-even for w > 38 in
algos::mg_divide::div_wide_pow10_chain_with; promoted to production inmacros::wide_transcendentalround_to_storage_with. Audited at 642K cross-witness inputs across all 6RoundingModes. Cite Möller–Granlund 2011 IEEE TC 60(2). limbs_mul_u64_into<L, LP1>primitive inwide_int/mod.rs— Knuth Algorithm M specialised to n = 1. Wired into the wide-tiermul_uchoke-point.- f64-bridge Newton seed for
sqrt+cbrtat the wide-tier macro core. Cite Brent–Zimmermann 2010 §5.4. benches/agm_vs_taylor_d{230,616,924,1232}.rs— reproducible AGM-vs-artanh probe at deep SCALE.
Changed¶
exp(-v)→1/exp(v)reciprocal-divide identity in sinh / cosh / tanh cuts the secondexp_fixedcall; ~2–2.4× at wide tier, ~1.5× at D38.Fixed::sqrtPythagorean jointsin_cosrefactored to shared-reduction-only at D38 —sqrtat the 256-bit Fixed costs more than a second Taylor at this width; reverted to shared-reduction. (Distinct from the wide-tiersqrt_fixedwhere the Pythagorean still pays.)mul_ucentral choke-point routes through the newlimbs_mul_u64_intoprimitive whenn ≤ u64::MAX; D76<35>ln−11 % (the choke-point lives inscale_by_kwhich is called multiple times per call).- D18 (i64)
mul/divfor SCALE ≥ 10 now uses a two-limb base-2^64 schoolbook divide (Knuth Algorithm D) instead of the__divti3soft-call; D18<18> mul −60 % (9.2 → 3.5 ns), div −47 % (9.8 → 5.1 ns).
Fixed¶
- Several
/ lit(2)divisions in inverse-trig and hyperbolic paths replaced with>> 1;q % lit(2)replaced withq.bit(0);v / d + v % dreplaced withv.div_rem(d). ~30 sites across the wide-transcendental macro and the policy / trig dispatch.
Performance — findings¶
- Tang
lnladder peaks at D1232<615>: 33.8× over v0.4.0 at the slot's exact SCALE. The ladder is monotone in tier except D924<461> where the larger table OOSes L2 on most x86 (still 30.8× over v0.4.0). - Smith r/2^n joint sin/cos Taylor LOSES at narrow / medium
tier. Three-way confirmed across D38, D57, D76. Only
theoretical wins start at D307+ (where the un-reduced Taylor
balloons to 50+ terms). We retain the adaptive in-kernel Smith
path in
exp_fixed. - Comba diagonal-layout schoolbook DIES at every n. Production
row-major LLVM-unrolled
limbs_mul_u64_fixedwins 14–92 % at n = 2..16. - Karatsuba / Toom–Cook crossovers don't fire at any shipped
width per
ROADMAP.md— schoolbook + LLVM unroll plus the recursive split's heap allocs push the crossover past D1232. - Brent's AGM
lncrossover empirically located at D1232 SCALE 1000 — 3× past the textbook ~300-digit prediction. Our chain-MG + narrow-GUARD artanh kernel is well-tuned enough to delay the crossover. AGM not wired (precision lift required first; reserved for 0.5 cycle). Cite Brent 1976 JACM 23(2), Salamin 1976 Math Comp 30. - D9 (i32) genuinely exhausted — LLVM inlines Granlund–Möller
for
/ 10^9already.
[0.4.1] — 2026-05-19¶
Removed¶
DecimalConststype alias — the deprecated alias for theDecimalConstantstrait (deprecated since 0.3.3 with note "removed in 0.4") is gone. Callers shoulduse decimal_scaled::DecimalConstants;instead. The trait surface, per-width impls, and allpi()/tau()/e()inherent methods are unchanged.
[0.4.0] — 2026-05-19¶
The 0.4 release lands four roughly independent strands in one cycle:
a mechanical breaking rename + scale-cap so every width name equals
its storage's maximum decimal-digit count and at least one integer
digit is always representable; a wide-tier transcendental rewrite
that lifts a unified algorithm library into src/algos/ and routes
every typed shell through per-family policy traits in src/policy/;
a src/ reorganisation into six narrative buckets (types/,
identity/, algos/, policy/, wide_int/, macros/,
support/); and a supply-chain / governance pass (OpenSSF Best
Practices "Passing", OpenSSF Scorecard, cargo-audit + Dependabot
in CI, SECURITY.md, REUSE-compliant LICENSES/). Performance
work landed alongside: D38 transcendentals "borrow" the wider D57
work integer to shrink the strict path 5–43×, bespoke D57 lookup
kernels carry the SCALE 20 / 44–56 cells, and the wide-tier
ln / exp / sin numbers improve 34–244× versus the 0.2.5
baseline (see docs/benchmarks.md §3 and the History section).
Breaking¶
- Six widths renamed to match the true digit ceiling of their underlying storage:
| Old | New | Storage |
|---|---|---|
D56 |
D57 |
Int192 |
D114 |
D115 |
Int384 |
D461 |
D462 |
Int1536 |
D615 |
D616 |
Int2048 |
D923 |
D924 |
Int3072 |
D1231 |
D1232 |
Int4096 |
The seven widths whose name already matched (D9, D18, D38,
D76, D153, D230, D307) keep their names.
MAX_SCALEcapped atname - 1on every width. ADxxx<SCALE>withSCALE == namerepresents only|x| < magic_constant(no integer digit guaranteed). The cap restores the invariant that every representable value has at least one integer digit:
| Type | Old MAX_SCALE |
New MAX_SCALE |
|---|---|---|
D9 |
9 | 8 |
D18 |
18 | 17 |
D38 |
38 | 37 |
D57 (was D56) |
57 | 56 |
D76 |
76 | 75 |
D115 (was D114) |
115 | 114 |
D153 |
153 | 152 |
D230 |
230 | 229 |
D307 |
307 | 306 |
D462 (was D461) |
462 | 461 |
D616 (was D615) |
616 | 615 |
D924 (was D923) |
924 | 923 |
D1232 (was D1231) |
1232 | 1231 |
-
Maximum-SCALE alias types removed. Each width used to expose a scale alias at the old
MAX_SCALE; those aliases are now gone:D9s9,D18s18,D38s38,D57s57(wasD56s57),D76s76,D115s115(wasD114s115),D153s153,D230s230,D307s307,D462s462(wasD461s462),D616s616(wasD615s616),D924s924(wasD923s924),D1232s1232(wasD1231s1232). The new ceiling is the one-less alias (D38s37,D76s75, ...). -
Per-scale construction macros for the old MAX_SCALE removed.
d9s9!,d18s18!,d38s38!,d76s76!,d153s153!,d307s307!no longer exist. The proc-macro entry points (d38!,d76!, ...) now rejectscale == namewith a clearer "scale N exceeds max for Dxxx (max = N-1)" error. -
Cargo feature flags renamed for the six renamed widths:
| Old | New |
|---|---|
d56 |
d57 |
d114 |
d115 |
d461 |
d462 |
d615 |
d616 |
d923 |
d924 |
d1231 |
d1232 |
The umbrella features (wide, x-wide, xx-wide) keep their names
and have been updated to reference the new per-width names.
Migration¶
For most users the upgrade is a mechanical search-and-replace:
- Rename type imports:
D56→D57,D114→D115,D461→D462,D615→D616,D923→D924,D1231→D1232. - Rename feature-flag mentions in your
Cargo.toml:d56→d57etc. - If you used a maximum-SCALE alias or wrote
Dxxx<MAX_SCALE>literally, drop the SCALE by one:D38<38>→D38<37>,D76<76>→D76<75>,D56<57>→D57<56>, etc. - The renamed per-scale macros are gone:
d38!(_, scale 38)→ write the value at SCALE 37 instead, or useD38<37>::from_bits(...)directly.
Fixed¶
- Wide-tier
FromStrceiling lifted. Decimal strings can now be parsed at every legalSCALEon every wide-tier width — previously a u128 intermediate inside the parser silently capped the parseable scale at 38, so e.g."0.5".parse::<D307<150>>()would fail with a precision error despite the type being well within storage range. The parser now widens through the storage integer directly, matching the round-trip contracts.parse::<T>() == Ok(T::from_str(&s).unwrap())at everySCALE ≤ MAX_SCALE.
Added¶
SECURITY.md— vulnerability reporting policy, supported versions table, scope, and disclosure timeline. Required for OpenSSF Best Practices conformance.CONTRIBUTORS.md— contributor acknowledgement file at the repository root.LICENSES/directory — REUSE-compliant layout. The top-levelLICENSE-MIT/LICENSE-APACHEtext files were moved intoLICENSES/MIT.mdandLICENSES/Apache-2.0.md, and a newLICENSES/THIRD-PARTY.mdenumerates third-party origins for the vendored algorithms.- OpenSSF Best Practices badge — project 12895 reached the "Passing" tier. README badge ribbon extended with the badge.
- OpenSSF Scorecard workflow (
.github/workflows/scorecard.yml) — weekly Scorecard run, results published as a repository badge. cargo-auditworkflow (.github/workflows/cargo-audit.yml) — RustSec advisory check on every push tomain, badge surfaced in the README. GeneratesCargo.lockin-CI since the repo gitignores it.- Dependabot configuration (
.github/dependabot.yml) — daily updates for Cargo, GitHub Actions, and thebench-history/sub-crate. - CodSpeed continuous performance tracking
(
.github/workflows/codspeed.yml) — wide-tier benches + a ULP precision gate run on every PR. bench-fullworkflow (.github/workflows/bench-full.yml) — matrix fanout across all 13 widths; each shard runs in its own runner so a multi-hour sweep no longer loses every completed width on a single VM eviction.bench-historyworkflow (.github/workflows/bench-history.yml) — cross-version harness inbench-history/thatcargo adds each shipped release into a stub crate and benches them under matched Criterion settings, driving the newdocs/benchmarks.mdHistory section and the per-widthdocs/figures/history/*.pngcharts.bench-trialworkflow (.github/workflows/bench-trial.yml) — focused single-cell trial bench for quick perf experiments without paying the full-matrix runtime.precisionworkflow (.github/workflows/precision.yml) — ULP precision gate against the in-treeexamples/ulp_report.rsbaseline; surfaced as a separate README badge.DynDecimalobject-safe façade — runtime-polymorphic trait that lets callers hold aBox<dyn DynDecimal>across different widths / scales when the concrete type is only known at runtime. Re-exported from the crate root.- Unified
D<S, SCALE>struct — every shipped width (D9 / D18 / D38 / D57 / D76 / D115 / D153 / D230 / D307 / D462 / D616 / D924 / D1232) is now a type alias over a single genericD<S, SCALE>carrier rather than a hand-rolled struct per width. The alias names and their public API surface are unchanged. - Per-family policy traits in
src/policy/—SqrtPolicy/CbrtPolicy/LnPolicy/ExpPolicy/PowPolicy/TrigPolicy. The typed shell for each width picks its kernel byimpling the matching policy trait against routines fromsrc/algos/, instead of macro-emitting one open-coded copy of the kernel per width. - Algorithm library in
src/algos/—sqrt/,cbrt/,ln/,exp/,pow/,trig/each ship the kernel variants the policies route to: awiden_to_d38path for the narrow tier, aborrow_d57path that promotes D38 to D57 for tighter strict kernels, afixed_d38path for the 256-bit handrolledFixed, awide_kernelfor D76+, and bespokelookup_d57_*tables for the SCALE 20 sqrt and SCALE 18–22 / 44–56 sincos / atan cells. borrow_d57bridge — D38 transcendentals (ln,exp,pow,sin,cos,tan,atan) gain a widen-to-D57 path that runs the strict kernel at the wider work integer and narrows back. Closes the SCALE 23+ accuracy gap and drops cost multiple × on the narrow tier (see Performance).docs/comparisons.md— moved-out home for the README's binary-vs-decimal-fraction explainer and the long-form comparison with other Rust decimal crates. Wired into the docs site nav alongsidewidths.md,getting-started.md,strict-mode.md, andfeatures.md.
Changed¶
src/reorganised into six narrative buckets. Files moved:types/— public typed shells (D9/D18/D38/D57/ …), the unifiedD<S, SCALE>carrier,traits/(theDecimal/DecimalArithmetic/DecimalConvert/DecimalTranscendental/DecimalConstants/DynDecimalsurface),arithmetic.rs,rescale.rs,num_traits.rs,overflow_variants.rs,powers{,_fast}.rs,log_exp{,_fast}.rs,trig{,_fast}.rs,consts/, andwidths.rs(the formercore_type.rs).identity/— cross-typePartialEq/PartialOrdbetween every width and the primitive integer / float surface (the former top-levelequalities.rs).algos/— kernel library described above, plusfixed_d38.rs(256-bitFixed, formerlyd_w128_kernels.rs) andmg_divide.rs(formerly top-level).policy/— per-family routing traits.wide_int/— unchanged in role; the hand-rolledInt192/Int384/ … storage family.macros/— unchanged in role; declarative + procedural macros that emit the typed shells.support/—error.rs,rounding.rs,display.rs,serde_helpers.rs,bench_alt.rs,diagnostics.rs.- Typed shells route through policy traits, not direct
algos::calls. EachDxxx::ln_strict(etc.) body is now a single<Self as LnPolicy>::ln_impl(self, …)dispatch, emitted via the existing macro surface. The layering shim that let typed methods reach straight into the kernel during the pilot is gone. - README — binary-vs-decimal-fraction explainer moved out
to
docs/comparisons.md; in-README copy slimmed to a one- paragraph pointer. Badge ribbon now carries OpenSSF Best Practices, OpenSSF Scorecard, and cargo-audit badges alongside the existing crates.io / docs.rs / MSRV / License / site / CodSpeed badges. Thedocs.ymlGHA badge is now labelled "site" to disambiguate from docs.rs. AGENTS.mdand.claude/skills/decimal-scaled/SKILL.mdrefreshed for the 0.4.0 layout: new width names, new src/ bucket map, the six-bucket layering rule (typed shells call policy, policy calls algos, algos may call wide_int but never the reverse).docs/benchmarks.md— every per-tier arithmetic table (§1) and strict-transcendental table (§3) regenerated from the v0.4.0 sweep. New History section (§7) plottingdecimal-scaledv0.2.5 → v0.4.0 deltas at D38 / D76 / D307 for the same operations. New §0.4.0 raw-data dump appended in place of the prior 0.3.2 dump. Run-conditions preamble spells out the GitHub-hosted runner caveat (1.5–2× wall- clock variance per Criterion's standing guidance).
Fixed¶
- Wide-tier
FromStrceiling lifted. Decimal strings can now be parsed at every legalSCALEon every wide-tier width — previously a u128 intermediate inside the parser silently capped the parseable scale at 38, so e.g."0.5".parse::<D307<150>>()would fail with a precision error despite the type being well within storage range. The parser now widens through the storage integer directly, matching the round-trip contracts.parse::<T>() == Ok(T::from_str(&s).unwrap())at everySCALE ≤ MAX_SCALE. D38<MAX_SCALE>.log10_strictno longer panics on inputs inside the storage range but close to the upper boundary. The intermediatemul_divstep was overflowing the narrow work integer at the boundary; the fixed path uses the wider work integer thatlog_strictalready uses.D57<MAX_SCALE>.cbrt_strictno longer overflows the work integer at the maximum scale. The cube-root reduction needed one extra guard digit at the storage ceiling.- Wide-tier
cbrt_strictno longer overflows the work integer atSCALE = MAX_SCALE. Generalises the D57 fix above to every wider tier. - Overflow panic messages across every narrowing kernel now spell out which kernel + which direction + the offending value, instead of the prior generic "rescale: scale-up overflow" formatting. Surfaces the right call site immediately in downstream stack traces.
mul_div_candidatesdivide rounding in the handrolled sanity-check path realigned with the production divide kernel. Previously the handrolled path rounded half-to-even off-by-one at the LSB on a small input class, masking real regressions in the production kernel.- Test gates promoted to module-level
#[cfg(...)]. A handful of test modules added in 0.3.x still carried runtimeif !COND { return; }early-returns; those are now module-level cfg gates so a test never silently no-ops under a feature combination that disables its precondition. - Infinite recursion in
DecimalTranscendentaltrait impl — one trait-method body called the trait method instead of the underlying inherent method, producing a runtime stack overflow when called via the trait. Fixed. Fixed::rescale_downargument order is nowdebug_assert- ed at call sites — a defensive guard added after a near-miss during the policy-trait wiring.
Removed¶
- Top-level layering shims —
src/lib.rsno longer re-exports symbols that have moved into the bucketed layout for backward source-path compatibility. Public callers using the documented crate-root re-exports (decimal_scaled::D38, etc.) are unaffected; only callers that reached into the private path layout need to update. research/references — none ever shipped in source, docs, or commit messages; the folder remains private and out of the crate tarball.
Performance¶
All numbers below come from docs/benchmarks.md (per-width
arithmetic tables in §1, strict transcendentals in §3,
cross-version History in §7). The 0.4 column reflects the
v0.4.0 GHA sweep on shared ubuntu-latest runners; cold-dev-box
re-runs typically measure 30–50 % faster on the wide tiers and
several × faster on the narrow tier's picosecond cells. Treat
the multiples as directional shape-of-change rather than as
drop-in benchmarks.
- D38 strict transcendentals "borrow" the D57 work integer.
D38
log_strict/log10_strict/exp2_strict/log_strict/powf_strictnow widen to D57 for the integer-kernel pass and narrow back. The D57 borrow path is also scale-gated for D38powf(SCALE >= 23, tightened from the originalSCALE >= 25after measurement). - Bespoke narrow-GUARD kernels on D57. Hand-rolled kernels
for the SCALE 20 sqrt (
algos::sqrt::lookup_d57_s20), the SCALE 18..=22 sincos (algos::trig::lookup_d57_s18_22_sincos), the SCALE 44..=56 sincos / atan (algos::trig::lookup_d57_s44_56_*) and the SCALE 45..=56 exp (algos::exp::lookup_d57_s45_56) carry the cells where the generic wide kernel was the wrong shape for the scale. - D38 cos closes ~25 % of the gap to D38 sin (joint sin / cos kernel re-tuned).
- D38 sqrt + div u128 fast path skips the 256-bit machinery when the inputs fit u128, recovering several ns per call on the narrow tier.
- Wide-tier transcendentals (D76+) —
pow10(w)memoised per-tier in the wide transcendental cores. The cross-version delta vs the 0.2.5 baseline on the same operations, fromdocs/benchmarks.md§3:
| op | 0.2.5 | 0.4.0 | speedup |
|---|---|---|---|
| D76<35> ln | 1.37 ms | 40.6 µs | 34× |
| D76<35> exp | 1.27 ms | 32.1 µs | 40× |
| D76<35> sin | 1.08 ms | 22.6 µs | 48× |
| D76<35> sqrt | 20.5 µs | 3.53 µs | 6× |
| D153<75> ln | 6.40 ms | 67.5 µs | 95× |
| D153<75> exp | 5.87 ms | 53.8 µs | 109× |
| D153<75> sin | 4.82 ms | 40.8 µs | 118× |
| D153<75> sqrt | 83.6 µs | 4.98 µs | 17× |
| D307<150> ln | 34.1 ms | 161.3 µs | 211× |
| D307<150> exp | 31.2 ms | 131.5 µs | 237× |
| D307<150> sin | 25.5 ms | 104.4 µs | 244× |
| D307<150> sqrt | 313 µs | 7.84 µs | 40× |
These multiples are conservative since the 0.2 column is a cold-dev-box run and the 0.4 column is shared CI; the algorithm-of-record changes (u64-native limbs, MG 2-by-1 reciprocal Knuth divide, Brent's two-stage exp argument reduction, multi-level sqrt halving in ln, [0, π/4] sin range reduction, sin_cos / sinh_cosh joint kernels, thread-local pi / ln2 / ln10 cache, pow10-cached mul / div per inner loop) carry the win.
Internal¶
- Build-time math-constant scales lowered:
SCALE_REFfor D153 / D230 / D307 dropped to 152 / 229 / 306 respectively (the others already matched the newname - 1ceiling). - Bench file names tracking width (
benches/full_matrix_d56.rs,benches/lib_cmp_d1231.rs, ...) renamed to match the new width identifiers. - New
src/macros/dyn_bridge.rscarries theDynDecimalblanket- impl bridge so every typed shell picks up the object-safe surface via one macro invocation. - Wide transcendental macro module (
src/macros/wide_transcendental.rs) consolidated to route throughsrc/policy/rather than emitting one open-coded kernel per width. - Layering rule codified: typed shells call policy, policy calls
algos, algos may call
wide_intbut never the reverse. Cargo.tomldev-dependencyfastnumbumped 0.4 → 0.7; benches follow the new API.
[0.3.3] — 2026-05-18¶
Trait surface split + benchmarks refresh, all in one cycle.
Changed¶
Decimaltrait is now a marker supertrait combining four narrower halves:DecimalArithmetic(operators, sign, pow / checked / wrapping / saturating / overflowing, reductions, plus the foundational type infoStorage,SCALE,MAX_SCALE,ZERO,ONE,MAX,MIN,multiplier()),DecimalConvert(from_bits/to_bits/scale, integer bridges, thestd-gated f64 / f32 bridge),DecimalTranscendental(the four-variant transcendental + root matrix), andDecimalConstants(pi/tau/e/ …). Decimal carries a blanket impl so every type that implements all four halves is Decimal for free; every shipped width (D9..D1232) keeps the full surface.
Callers using T: Decimal and T::method() syntax keep
compiling unchanged — method resolution finds methods via the
supertrait chain. The narrower bounds let generic code shrink
its surface when it doesn't need everything, e.g.
fn dot<T: DecimalArithmetic + Copy>(a: &[T], b: &[T]) -> T.
Breaking¶
<X as Decimal>::methodUFCS path callers need to move to the right sub-trait:<X as DecimalArithmetic>::ZERO/::MAX_SCALE/::multiplier/::sum/::is_zero/ etc., and<X as DecimalConvert>::from_bits/::to_int_with/::from_f64/ etc. Test files undertests/and the internal tests insrc/core_type.rshave been updated as part of this release.
Added¶
DecimalArithmeticandDecimalConvertas new narrower trait surfaces (see Changed). Re-exported from the crate root.
Benchmarks¶
docs/benchmarks.mdrefreshed from the 2026-05-18 sweep. Every per-tier arithmetic table (§1) and every strict- transcendental table (§3) regenerated with the new numbers, preserving narrative around them.- Caveats called out inline: narrow-tier (D9 / D18 / D38) picosecond-scale arithmetic sits near the bench machine's resolution floor and the cross-version deltas there are contention noise rather than real perf; narrow-tier transcendental values in this sweep also reflect contention drift (cold-machine micro-bench for D38<19> ln_strict measures ~54.8 µs vs the >800 µs in the full_matrix table — ~15× gap). Wide-tier numbers (D57+ arithmetic, D57+ transcendentals) are reliable and reflect real cumulative perf work from the 0.3.x cycle.
- The 326-measurement raw data dump from 0.3.2 stays appended at the end of the doc for anyone who wants every cell criterion produced.
[0.3.2] — 2026-05-18¶
API-additive surface expansion across every decimal width plus
several downstream-blocking defect fixes. No breaking changes;
existing callers compile unchanged. Big themes: the four-variant
_strict_with / _approx / _approx_with matrix on every
transcendental, mode-aware constants, full serde parity, the
construction-macro surface extended to every shipped tier, a docs.rs
build fix, and primitive ergonomics on the wide-int and
fixed-decimal types.
Headline perf number for the new _approx family on the D38<19> ln
sanity bench: 1.17× faster at guard 25, 1.45× at 20, 1.80× at 15,
2.62× at 10, 3.70× at guard 6 — all vs strict's 54.8 µs. Strict
itself is at parity with 0.3.1 (52.3 µs prior → 54.8 µs now, within
bench noise). Full per-tier numbers in 0.3.3.
Added¶
- Four-variant matrix on every transcendental. Each function
ships
<fn>_strict,<fn>_strict_with(mode),<fn>_approx(working_digits), and<fn>_approx_with(working_digits, mode). The_strictbody keeps a const-foldedSTRICT_GUARDso LLVM specialises one optimal kernel perSCALE;_approxtakes the guard width at runtime for callers who want to trade ULP precision for latency. Whenworking_digits == STRICT_GUARDthe_approx_withbody redirects to_strict_withso the fast path is never displaced. Covers ln / log / log2 / log10 / exp / exp2 / powf on D38 and the wide tiers, and sin / cos / tan / atan / asin / acos / atan2 / sinh / cosh / tanh / asinh / acosh / atanh / to_degrees / to_radians for the trig family. sqrt_strict_with(mode)/cbrt_strict_with(mode)/hypot_strict_with(mode)on D38 and the wide tiers. Roots have no working-digits parameter (the exact-integer-root path is precision-independent), so only the_strict/_strict_withpair exists.FloorandCeilingdispatch by sign forcbrt.DecimalConsts::*_with(mode)siblings.pi_with/tau_with/half_pi_with/quarter_pi_with/golden_with/e_withrescale the embedded reference under a caller-suppliedRoundingMode.pi_with(Floor)gives the largest representable value ≤ true π — useful for CAD tessellation that must not over-count.from_num/to_numparity across every width. The saturatingNumCast-style bridge previously existed only on D38; extracted intodecl_decimal_num_traits_basics!so D9 / D18 / D38 / D57 / D76 / D115 / D153 / D230 / D307 / D462 / D616 / D924 / D1232 all get the same surface.- Serde for every wide tier. Added
decl_wide_serde!invocations for D57 / D115 / D230 / D462 / D616 / D924 / D1232 — previously only D76 / D153 / D307 had Serialize / Deserialize. proc-macro-crateresolution in the construction macros (d9!,d18!,d38!,d76!,d153!,d307!). Lets consumer crates aliasdecimal-scaledfreely instead of being forced to use the literaldecimal_scaledimport name. Falls back to::decimal_scaledwhen the lookup fails — same behaviour as the original hard-coded path.- Construction macros for every wide tier. Added
d57!,d115!,d230!,d462!,d616!,d924!,d1232!— every shipped Decimal width now has a compile-time literal constructor over the existingexpand_for(Width, …)machinery, gated behind the matching feature. EPSILON/MIN_POSITIVEfor every decimal width. Macro- emitted out ofdecl_decimal_basics!so D9 / D18 / D38 / D57 / D76 / D115 / D153 / D230 / D307 / D462 / D616 / D924 / D1232 all share the same surface. Closes the "literal1doesn't coerce to a wide-int storage type" trap that forced downstream callers to writefrom_bits(<Int192>::from_u128(1))longhand.- Wide-int
From<primitive>impls. Every signed wide int ($S) getsFrom<u8/u16/u32/u64/u128/i8/i16/i32/i64/i128>; every unsigned ($U) getsFrom<u8/u16/u32/u64/u128>plus afrom_u128const-fn constructor. - Wide-int
From<float>impls.From<f32>andFrom<f64>for both$Uand$S; nightly-onlyFrom<f16>/From<f128>gated behind the existingexperimental-floatsfeature. Same saturate-on-overflow / NaN-maps-to-zero contract the decimal-tier float bridge uses. - Wider scale-alias coverage. Backfilled D76 / D153 / D307
from the previous 5-8 aliases each to ~25 per tier covering the
standard decimal-precision breakpoints (1, 2, 3, 4, 6, 9, 12,
15, 18, 20, 24, 28, 32, 35, 38, 50, plus tier-specific midpoints
and the storage-max scale). Added
Dnns1(one fractional digit) to every wide tier so all 13 widths have a consistent scale-1 alias. AddedD57s20for the 20-digit CAD-numeric precision moocad's Stage 5 needed.
Fixed¶
- docs.rs build for 0.3.1 failed with
E0425: cannot find type D924.src/core_type.rs'sD616::widenreturnedD924<SCALE>unconditionally, butD924is gated behindxx-wide— which the docs.rs feature set didn't enable. SplitD616's impl block in two sowidenonly exists when D924 does, and addedxx-wideto the docs.rs[package.metadata.docs.rs]feature list for a complete rendered surface.
Documentation¶
- f64-boundary warning on
DecimalConsts:to_f64()is itself correctly rounded, butpi() → to_f64()atSCALE < 15lands ~466 ULPs fromstd::f64::consts::PIbecause the decimal value is intrinsically coarser than the f64 grid. Downstream code that uses the f64 to count, bucket, or seed an iteration loop should source mathematical constants fromstd::f64::constsdirectly at the boundary, or pickSCALE ≥ 15. - Module-header rewrite on
log_exp_strict,trig_strict,powers_strict, and the wide-tierwide_transcendentalmacro describing the four-variant matrix and when each form is appropriate.
Benches¶
- Split the monolithic
full_matrixbench into per-width binaries (full_matrix_d{9,18,38,56,76,114,153,230,307,461,615,923,1231}) sharing afull_matrix_common.rsmacros module. A multi-hour sweep used to lose all completed widths on a power-loss / lid close; per-width binaries let criterion flush each width before starting the next. scripts/bench_pinned.ps1andscripts/bench_sweep.ps1: 2-lane parallel runner that pins each lane to a physical core (both SMT siblings) at High priority. Skips any bench whose log already shows completion, so a partial sweep resumes from where it left off in one command.
[0.3.1]¶
Release-process patch. Library code, public API, on-wire format, and bench numbers are byte-identical to 0.3.0.
Fixed¶
- GitHub Pages docs site (
mootable.github.io/decimal-scaled) failed to refresh on the v0.3.0 tag push. The Pages environment protection rule only allows deploys frommain; the concurrent main-push run that was allowed to deploy got cancelled by the tag-push run that arrived right after. Tag-triggered deploys blocked. 0.3.1 ships as amain-branch push so the docs workflow runs to completion.
Notes¶
- Future releases should land the version-bump commit, let
mainbuild + deploy the docs, then tag — not the other way round. Will codify inscripts/deploy.ps1.
[0.3.0]¶
The half-width-tier release. The decimal ladder now goes
D9 → D18 → D38 → D57 → D76 → D115 → D153 → D230 → D307 →
D462 → D616 → D924 → D1232 — every adjacent pair has a
lossless From / widen() plus a fallible TryFrom /
narrow(). New x-wide and xx-wide Cargo umbrellas gate the
wider ranges so default builds stay lean.
The strict / fast dispatcher rule changes too: strict is now
the default plain dispatch in every build, and wins on tiebreak
when both strict and fast are enabled. The only way to
land plain sin / ln / sqrt etc. on the f64 bridge is a
deliberate three-step opt-out (default-features = false + add
fast + add std + don't re-add strict). The named
*_strict and *_fast methods stay available regardless of
feature choice.
Added — new decimal tiers¶
D57(192-bit),D115(384-bit),D230(768-bit) — half-width tiers between every existing power-of-two width. Gated behindd57/d115/d230individually or by the expandedwideumbrella (nowD57–D307together).D462(1536-bit),D616(2048-bit) — new x-wide tier, gated behindx-wide(ord462/d616individually).D924(3072-bit),D1232(4096-bit) — new xx-wide tier, gated behindxx-wide(ord924/d1232individually).- The naming rule: the number on every
D{N}type is the highest safeSCALE(MAX_SCALE) the storage can hold, i.e. the number of decimal digits you can represent without overflow. SoD1232meansMAX_SCALE = 1232(∼ 1232 decimal digits of headroom), not "1231 bits". - Comprehensive scale aliases per the new tiers: ≥ 16 per tier above the narrow range, covering 0 / common midpoints / the previous tier's MAX_SCALE as a cross-tier sentinel / the new tier's MAX_SCALE.
Changed — breaking¶
D38.widen()now returnsD57<SCALE>instead ofD76<SCALE>. Symmetrically,D76.narrow()→D57,D76.widen()→D115,D153.narrow()→D115,D153.widen()→D230,D307.narrow()→D230, andD307.widen()is new (→D462, gated behindx-wide). The legacy power-of-two-next-up semantics are gone; the comprehensive ladder is the new default. Callers that need the old jump can use.into()/.try_into()to skip rungs.strict+fastnow resolves to strict. Previouslyfastwon the tiebreak. The strict path is now fast enough (ln_strictat D38<19> is ~1.5 µs) that staying on the deterministic correctly-rounded path by default is the right call across more codepaths.(no feature)builds now dispatch plain transcendentals to*_stricttoo (previously they fell through to*_fast). Same reasoning: strict-by-default unless deliberately opted out.
Added — performance¶
- Chain-of-÷10^38 rescale for wide-tier
mulatSCALE > 38: factorsn / 10^SCALEas a sequence ofn / 10^38chunks, each riding the existing base-2^128 MG 2-by-1 magic kernel. Combined-remainder bookkeeping preserves HalfToEven correctness across chunks. Measured wins: - D307<150> mul: 786 ns → 434 ns (1.8× faster)
- D462<230> mul: 1.62 µs → 866 ns (1.9×)
- D616<308> mul: 2.20 µs → 1.36 µs (1.6×)
- D924<461> mul: 3.30 µs → 2.68 µs (1.2×)
- D1232<616>: marginal (chain length eats the per-pass win; needs Barrett or wider magic tables — tracked for 0.3.x).
d57/d115/.../d1232per-tier features can be enabled individually if you don't want a full umbrella.
Added — benches + tooling¶
- Per-width
lib_cmp_d{N}benches — 13 new bench binaries (cargo bench --bench lib_cmp_d307) replace the monolithiclibrary_comparison.rs. Each tier runs in minutes instead of hours; iterating on one tier's perf doesn't need a full matrix sweep. Shared macros + helpers live inbenches/lib_cmp_common.rs. benches/quick_div.rs— focused microbench for D307/D616/D924/D1232 div + mul. Used during the wide-tier perf tuning passes.- Per-width summary chart family — one PNG per power-of-two
storage width (
docs/figures/library_comparison/summary_{N}bit.png) showing every op (add / sub / neg / mul / div / rem / sqrt / ln / exp / sin / cos / tan / atan / sinh / cosh / tanh) on a log-y axis with one bar per library. Re-rendered automatically via thescripts/refresh_bench_artifacts.shworkflow. scripts/bench_log_to_medians.py— extracts the criterion medians from any number of bench-log files intotarget/medians.tsvso chart_gen.rs picks up the latest run.- chart_gen filter tightened: only renders multi-library line charts where ≥ 2 libraries have ≥ 2 data points, so scatter-of-dots charts get dropped automatically.
- Trig family in the bench matrix —
cos/tan/atan/sinh/cosh/tanhbenched for every peer that ships them (decimal-scaled, fastnum, g_math, rust_decimal cos/tan). examples/rounding_mode_probe.rs— diagnostic that prints the candidate renderings under each rounding mode forexp(1),sin(1),ln(2),sqrt(2). Used to verify that the §5 "1 ULP" entries onfastnum/rust_decimalwere render-mode artifacts (they carry the correct value internally), not computation errors.examples/fast_vs_strict_ulp.rs— per-tier accuracy-loss table for*_fastvs*_strict. Drives the new §2.1 indocs/benchmarks.md.
Fixed¶
- D57 work-integer overflow — D57's transcendental work
integer was Int512, which couldn't carry the squared
intermediate at
SCALE + GUARD = 86working scale. Bumped to Int1024. Caught by the in-flight bench sweep. feature = "xx-wide"-only builds — several macro arms insrc/macros/full.rsandsrc/mg_divide.rsgated only onwide/x-wideand missedxx-wide/d924/d1232, so anxx-wide-only build failed to compile. Gates extended.
Docs¶
- §5 in
docs/benchmarks.mdrewritten as "Where each crate fits" — feature-matrix-first framing, per-storage-width summary charts, an explicit note distinguishing render-mode artifacts (fastnum / rust_decimal / decimal-rs) from real precision losses (dashu-float 4 ULP exp(1), g_math 6–46 ULP). Bench-doc tone shifted from "competition" to "here's where each crate fits, here's where ours fits". docs/widths.md,docs/getting-started.md,docs/strict-mode.md,docs/features.mdall updated to enumerate the thirteen-tier ladder and describe the new strict-by-default dispatcher.- README — "Two headline guarantees" lede now promotes both ≤ 0.5 ULP correctness AND caller-chosen rounding mode at every lossy operation; tier table extended to all 13 widths.
ALGORITHMS.md— wide-int section enumerates every shipped storage type; atan halvings table extended to cover the new wide tiers; tier-listing references updated from the old four-tier list.ROADMAP.md— explicit versioning intent table (0.3.0 done / 0.4.0 signed SCALE + RNG / 1.0.0 gated on competitive wide-tier mul/div or per-row documented gap); MG magic-multiply extension + Barrett path queued for 0.3.x; out-of-tree ecosystem crates (decimal-scaled-expr / -math / -finance) with the expression-engine dual-track + whole-tree serialisation design captured.
[0.2.5]¶
Docs + benchmark accuracy patch. Library code, public API, and on-wire format are byte-identical to 0.2.4.
Fixed¶
docs/benchmarks.md- every numeric cell in the arithmetic, fast-transcendental, strict-transcendental, and wide-integer-backend tables was re-measured on a single machine in one criterion run with the default 3 s warm-up, 50-sample (D38-and-narrower) or 20-sample (wide tier) windows. The previous numbers were collected with a much shorter warm-up / fewer samples and several rows shipped unsubstituted__LOSSY_*__/__STRICT_*__template placeholders.benches/decimal_backends.rs- theD128_lossyandD256_lossyrows called the plain*dispatcher methods, which with the defaultstrictCargo feature flip to the*_strictinteger kernel. The rows therefore measured the strict path twice instead of contrasting fast vs strict. They now call*_fastexplicitly, so the fast / strict distinction shown in the docs is honest.
Changed¶
docs/benchmarks.md§2 "Fast transcendentals" - table reshaped from the unsubstituted "D9 / D18 / D38 fast" placeholders to the actually-benched "D38*_fast/ D76*_fast/rust_decimal" comparison, with a prose note that D9 / D18*_fastshare the D38 f64-bridge kernel viato_f64/from_f64and incur only a sub-ns round-trip on top of the listed D38 numbers.docs/benchmarks.mdmethodology section - warm-up / sample-size text updated to match the bench harness's actual configuration (3 s warm-up, auto-tuned measurement window, 50 or 20 samples depending on tier).docs/benchmarks.md§3 strict transcendental tables - collapsed from one column per (width, scale) to one column per width, each cell showing only the s = mid measurement (the honest series-cost scale - s = 0 hits fast-path early returns and s = max sometimes shortens via Cody-Waite range reduction, so neither is a fair comparator). The chosen mid is listed in the column header (e.g.D76 (s=35)).docs/benchmarks.mdTime units table - added picosecond row and reframed the third column as "Relative to a second" instead of "Relative tons" for consistency across the table.docs/benchmarks.mddata-cell rendering - scientific notation in data cells (e.g.1.46×10⁻³ µs) replaced with plain decimals (0.00146 µs). Scientific notation is now reserved for values smaller than 10⁻⁵ of the row's unit (none in the current tables). Time units table is unchanged (still uses10⁻³ s,10⁻⁶ s, etc. for second relationships).
Added¶
benches/library_comparison.rs- new bench that pitsdecimal-scaledagainst every viable peer on crates.io (fastnum,bigdecimal,dashu-float,decimal-rs,rust_decimal,fixed::I*F*,g_math) across all six decimal-scaled width tiers (32 / 64 / 128 / 256 / 512 / 1024 bit) at three scales per tier (s=0, s=mid, s=max).examples/ulp_report.rs- one-shot accuracy report measuring ULP error for each library'sln(2)/exp(1)/sin(1)/sqrt(2)against aD76<19>integer-only*_strictbaseline. Confirmsdecimal-scaledis 0 ULP on every transcendental tested and showsg_math's "0 ULP transcendentals" marketing claim is 6–46 ULP off at the matched precision.examples/chart_gen.rs- pure-Rust (plotters) chart generator that readstarget/medians.tsvand emits one layered line chart per (op × width) todocs/figures/library_comparison/. 60 PNGs total; the meaningful-variation subset is embedded indocs/benchmarks.md§5.docs/figures/library_comparison/*.png- 52 generated charts (every op × width combination that has measurements).docs/benchmarks.md§5 Library comparison - new chapter with one speed table per width tier (s=mid representative scale, library-by-library), an accuracy table at the 128-bit tier (ULP errors for every supported transcendental across every library), embedded charts for the meaningful-variation ops, and a "reading the comparison" buyers-guide paragraph that maps "what do you need" → "which crate".- Dev-dependencies:
fastnum,bigdecimal,dashu-float,decimal-rs,scientific,plotters- all bench/example-only, none compiled into a normal build. ROADMAP.md(repo root) - tracked list of throughput gaps surfaced by the §5 library comparison and the planned fix per item (Burnikel-Ziegler divide, Karatsuba/Toom-3 mul,*_approx(working_digits)transcendental family). Cross-linked fromdocs/benchmarks.mdRoadmap section.
[0.2.4]¶
Agent-ecosystem additions. No library code changes - the crate's public API, behaviour, and on-wire format are byte-identical to 0.2.3. The bump exists so the new agent-facing assets ship in the crates.io tarball.
Added¶
AGENTS.md(top level) - tool-agnostic usage guide consumable by Cursor, Continue, Aider, Codeium and any other agent runner that crawls a repo forAGENTS.md. Covers width / scale picking, the strict-vs-fast dual API, rounding modes,DecimalConsts, rescaling, serde format, common anti-patterns, Cargo feature cheat sheet, and quick recipes..claude/skills/decimal-scaled.md- Claude Code skill (same content asAGENTS.md) withname/descriptionfrontmatter so Claude Code can auto-discover and invoke the skill when a user prompt mentions the crate.
[0.2.3]¶
Documentation patch (and matching test additions). The 0.2.2 docs
incorrectly listed golden among the constants that don't fit
D38<38>'s storage range - the code was correct (golden ≈ 1.618 is
inside the ±1.70141 storage range, so the method returns the
correctly-rounded value), but the prose and CHANGELOG copy claimed it
panicked. Fixed.
Fixed¶
- Documentation in
consts.rs(module preamble +DecimalConststrait doc) andCHANGELOG.mdfor 0.2.2 said the larger-magnitude constants that overflowD38<38>storage were "pi, tau, e, golden".golden ≈ 1.61803actually fits the ±1.70141 storage range and the method returns the correctly-rounded value; onlypi(3.14),tau(6.28), ande(2.72) overflow. Docs corrected.
Added¶
tests/consts.rsinline mod: explicit#[should_panic]tests pinningD38<38>::pi(),D38<38>::tau(), andD38<38>::e()to the storage-overflow panic. Promotes the prior single-constant spot test to cover all three.- The
fitting_constants_at_scale_38_are_correctly_roundedtest now also assertsD38<38>::golden()is correctly rounded to 0.5 ULP (= 1 LSB) of the canonical 38-digit value.
[0.2.2]¶
DecimalConsts 0.5-ULP contract is now uniform across every supported
scale - the 0.2.0 / 0.2.1 ≈ 5 ULP "exception at D38<38>" is gone.
Changed¶
DecimalConstsreference precision - every constant on D9 / D18 / D38 is now derived from the 75-digitInt256reference (the same one the wide tier already used), rescaled down half-to-even to the caller'sSCALE. The previous code used a 37-digiti128reference and rescaled upward by 10 atD38<38>, which appended a placeholder zero and left the result ≈ 5 ULP off the canonical value. Every result on every supported scale on every width is now within 0.5 ULP of the canonical decimal expansion - the precision contract holds with no documented exceptions.D38<38>storage-range overflow - atSCALE = 38the D38 storage range is approximately ±1.7, so the four larger-magnitude constants (pi ≈ 3.14,tau ≈ 6.28,e ≈ 2.72,golden ≈ 1.62) genuinely cannot be represented. The corresponding methods previously panicked with the generic rescale messageD38::rescale: scale-up overflow; they now panic with the explicitD38 constant out of storage range: <name> cannot fit i128 at SCALE = 38.D38<38>::half_pi()andD38<38>::quarter_pi()(which fit storage) are correctly rounded to 0.5 ULP - verified by a new test asserting|result − truth| ≤ 1 LSBat the 38-digit storage scale.
Fixed¶
- The "
D38<38>≈ 5 ULP exception" mentioned in 0.2.1'sDecimalConstsmodule / trait docs is removed from both the preamble and the trait blurb; the rewritten docs state the now- uniform 0.5 ULP contract. docs/strict-mode.md"Choosing the configuration" table reflowed: the default isstrict(not the f64 bridge), and thefastfeature row no longer claims it drops the*_strictsurface (corrected in 0.2.0 elsewhere; the table row was missed).
[0.2.1]¶
Documentation patch - no API changes.
Fixed¶
- docs.rs build: the rendered crate page on https://docs.rs/decimal-scaled
was only showing the default-feature surface, so the wide-tier types
(
D76/D153/D307), thedNN!proc-macros, and theserde_helpersmodule were missing. Added a[package.metadata.docs.rs]block that enablesstd,serde,strict,macros,wide, andx-widefor the docs build. The matchingdocsrscfg is also wired so future#[cfg(docsrs)]doc-cfg badges can highlight feature-gated items. constsmodule +DecimalConststrait docs: the preamble and per- method blurbs claimed every constant was rescaled from a single 37-digiti128reference. That was true for D9/D18/D38 but ignored the per-tier raw references shipped for D76 (75 digits), D153 (153 digits), and D307 (307 digits) underconsts_wide.rs. The new docs include a per-tier reference table and an explicit statement of the precision contract: within 0.5 ULP at every supported scale on every width, with the documented exception ofD38<38>(the D38 maximum, rescaled upward by 10 from the 37-digit reference - ≈ 5 ULP bound onpi/tau/e/golden).
[0.2.0]¶
The 0.2 release rounds out the family the 0.1 line scaffolded: every
width ships the full method surface, the Decimal trait carries the
width-generic API, and the strict / fast routing is symmetric and
explicit.
Added¶
- Wide tier (D76 / D153 / D307) - the 256 / 512 / 1024-bit decimal
widths are now feature-complete. Each implements every surface D38
has: arithmetic and bitwise operators, sign methods, integer
methods, overflow variants, pow + powi + the four pow overflow
variants, cross-type
PartialEqagainst every primitive integer and float, the float bridge (from_f64,from_f64_with,to_f64,to_f32), serde round-trip, and the full strict-transcendental surface - every*_strictmethod plus a mode-aware*_strict_with(mode)sibling. Two AGM alternatesln_strict_agm/exp_strict_agm(Brent–Salamin 1976, Newton-on-AGM-ln) are exposed alongside the canonical artanh / Taylor paths. - In-tree wide-integer module (
crate::wide_int) - the wide tier is now backed by a hand-rolledInt256/Int512/Int1024/Int2048/Int4096family (plus unsigned siblings) emitted by a macro. No external big-integer dependency. Includes Karatsuba multiplication (dispatched at the 16-limb threshold), Knuth Algorithm D, and a Burnikel–Ziegler recursive divide wrapper. Decimaltrait - expanded surface - the trait now carries every uniform method every width implements: arithmetic, bitwise, and shift operators as supertrait bounds; sign (abs,signum,is_positive,is_negative); integer methods (div_euclid,rem_euclid,div_floor,div_ceil,abs_diff,midpoint,mul_add); integer-shape predicates (is_nan,is_infinite,is_finite); the full pow + checked/wrapping/saturating/overflowing pow family; the fullchecked_*/wrapping_*/saturating_*/overflowing_*ofadd/sub/mul/div/neg/rem; integer conversion (from_i32,to_int,to_int_with); the float bridge gated onstd; and default reductions (is_zero,is_one,is_normal,sum,product). PlusDebug/Display/Hashsupertraits.d9!/d18!/d76!/d153!/d307!proc-macros - matchingd38!per-width entry points, including:- per-scale wrappers (
d38s12!,d18s6!, etc.) that pre-bake the scale qualifier; - radix prefix integers (
0xFF,0o755,0b1010); - the explicit
radix Nqualifier; - fractional radix literals (
d76!(1.A3, radix 16, scale 12)); - explicit
scale Nandroundedqualifiers. *_strictand*_fastnamed methods always available - both surfaces compile in every feature configuration (subject only to dependency gates -*_fastneedsfeature = "std"). The plain*form is the only thing thestrict/fastfeatures control.widen()/narrow()hop methods - promote to the next storage tier or demote with a fallible narrowing, without the longhandFrom::from/TryFrom::try_fromsyntax.rescale_with(mode)mode-aware sibling on every width.with_scale<TARGET>()builder-style alias forrescale.*_with(mode)siblings throughout - every default-rounding operation (from_f64,to_int,rescale, etc.) now has a sibling taking an explicitRoundingMode.from_num/to_numon D38 (insrc/num_traits.rs, renamed fromfixed_compat) - saturating, never-panicking constructors and readers that thread throughnum_traits::NumCast.hypot_stricton every width - integer-only, correctly-roundedsqrt(a² + b²)via the scale-trick algorithm.
Changed¶
- Type names - public types now name themselves by safe decimal
digit capacity (
D9/D18/D38/D76/D153/D307) rather than by underlying integer bit-width. The number in the type name is also the type'sMAX_SCALE. - Strict mode is the default -
default = ["std", "serde", "strict"]. Build without default features to get the f64-bridge path. *_fast(formerly suffix-free) - the f64-bridge methods are now named*_fast(ln_fast,sin_fast, …) for symmetry with*_strict. Plain*is the feature-driven dispatcher.fastfeature contract - no longer drops the*_strictsurface; only forces plain*to resolve to*_fast.Decimaltrait supertrait bounds - extended withDefault,Debug,Display,Hash, all arithmetic /*Assignoperators, and the full bitwise / shift operator set.fixed_compat.rs→num_traits.rs- file renamed; module docs no longer reference thefixedcrate. Thefrom_num/to_nummethods themselves are unchanged.- README, docs/, and crate-level documentation rewritten to reflect the all-six-widths reality. Stale claims about D38-only-implements-trait, bnum-backed wide tier, "wide tier not yet wired", "Karatsuba is a future optimisation", and "fast drops the strict surface" are all corrected.
Removed¶
- The
bnumdependency - wide-tier storage migrated to the in-treewide_intmodule.bnumand friends remain as[dev-dependencies]for the benchmark baselines only. _lossy/_fastfloat-conversion suffixes - the float conversion methods are nowto_f64,from_f64,to_f32,from_f64_with. The historic_lossy/_fastsuffixes were redundant since there is no strict counterpart for these (they are type conversions, not transcendentals)._lossy/_intinteger-conversion suffixes dropped for the same reason -from_int/to_int/to_int_withare the only variants.- Placeholder wide-tier feature flags (
d115,d230,d462,d616,d924,d1232) - these were forward-planned widths that were never implemented. Shipping no-op feature flags would mislead users pinning to them. Will be re-added when the corresponding storage types land. - Dead code in
mg_divide- the unuseddiv_exp_fast_2wordwrapper (only the_with_remvariant has callers). - Inline test mods that ran without asserting - the runtime
if !DEFAULT_IS_HALF_TO_EVEN { return; }guard pattern was replaced with module-level#![cfg(...)]so tests never silently no-op under a non-defaultrounding-*feature.
Fixed¶
- Strict/fast routing defect - pre-0.2 the
*_strictmethods werecfg(not(feature = "fast"))and the*_fastmethods werecfg(all(feature = "std", any(not(feature = "strict"), feature = "fast"))), so in the default-strict build there was no way to call the f64-bridge methods by name, and vice versa. Both surfaces now always compile (subject tostdfor*_fast). - Module-level doc comment staleness - six modules contained D38-only narratives / "Phase N will add" / "future widths" / broken file-path references; rewritten to match the all-six-widths reality.
- Broken intra-doc links -
[Self::MIN]at module scope,[FromStr]withoutcore::str::prefix,[D38::rescale]at module scope,[num_traits::Zero]shadowed by the post-renamecrate::num_traitsmod - all fixed.cargo doc --no-deps --document-private-itemsnow reports zero warnings. - Crate-wide warning-clean build under every feature
combination -
default,--no-default-features,fast,strict,wide,x-wide, and combinations thereof. - Coverage hardening - comprehensive functional tests added for
every public surface, the wide-integer kernels,
mg_divide, the guard-digitd_w128_kernels, and every transcendental's domain panic. Tests are functional (named by behaviour, not by uncovered line) and topic-organised intests/.
Compile-time / MSRV¶
- MSRV declared as Rust 1.85 (lower bound for the 2024 edition).
Migration notes¶
- The
D128(etc.) type names are gone - they were renamed to their digit-capacity counterparts in the 0.1 line. If you pinned to a pre-rename name, update to the new spelling. - Code that called
.ln()/.sin()etc. and relied on the f64 bridge being present in the default strict build now still compiles, but the routing has been clarified - call.ln_fast()/.sin_fast()directly if you specifically want the f64 path regardless of the build's feature set. - The
_lossy/_fastsuffixes on float conversion methods (to_f64_lossy,from_f64_fast, …) have been removed across two prior renames; the methods are now justto_f64/from_f64/ etc. Update any leftover suffixed call sites. - If you depended on a placeholder wide-tier feature flag (
d115,d230,d462,d616,d924,d1232), the flag no longer exists. Usewideorx-wideto cover the implemented widths.
[0.1.1] - 2025-12¶
Bug-fix release of the initial public 0.1 line. Refer to the git
history under tag v0.1.1 for the full commit log; the changes
focused on the repo URL / documentation metadata.
[0.1.0] - 2025-12¶
Initial public release. Established the core D38<const SCALE: u32>
type, the strict-vs-fast transcendental dual API, the 256-bit
Möller-Granlund magic-number divide path for mul/div, the
correctly-rounded sqrt / cbrt via exact-integer radicand, the serde
helpers, the d128! macro, and the docs/benchmarks scaffolding.