Address opendp-num fuzz findings (DASHU-007/008/015/022/026)#94
Conversation
Reviewed the opendp-num differential/property-fuzz findings against the current source. DASHU-020/021 are already fixed by PR #91; DASHU-023/024 (directed rounding at FBig's exponent-range extremes) are tracked with TODO comments and deferred. This commit fixes the remaining five. DASHU-015 (dashu-int, incorrect-result): `to_f64_small` reported an inexact conversion as `Approximation::Exact` at the `DoubleWord::MAX` boundary. The exactness test used a saturating `f as DoubleWord` round-trip, which clamps back to `DoubleWord::MAX` when the value rounds up to `2^BITS`. Detect that saturation case before the comparison; apply the same guard to the 16/32-bit RefLarge -> f64 fast path. DASHU-026 (dashu-float, debug panic/OOM): `round_fract`'s debug assert built `B^precision`; for a sparse sticky tail (precision == the exponent gap, e.g. from `exp_m1` of a large-magnitude input) this exhausted memory in debug builds. Replace it with a `log2_bounds`-based precondition check that allocates nothing and only fires on a proven violation. DASHU-022 (dashu-float, panic): `assert_limited_precision` fired before the exact zero/one shortcuts, so values carrying unlimited precision (precision 0) -- `FBig::try_from(0.0)`, `FBig::ONE`/`ZERO` -- panicked in exp/exp_m1/ sqrt/ln/ln_1p despite mathematically exact results. Hoist the exact shortcuts above the assertion. DASHU-008 (dashu-int, panic): GCD of two large, similarly-sized integers aborted with "internal error: not enough memory allocated". The scratchpad was reserved once from the initial operand lengths, but each euclidean step's division dispatches on the current lengths, so a later lopsided Burnikel-Ziegler step (divisor > 48 words, quotient > 32 words) was under-reserved. Size it for the worst case reachable in the loop: `mul::memory_requirement_up_to(rhs_len, rhs_len / 2)`. Applies to both the gcd and extended-gcd reservations. DASHU-007 (dashu-float, missing feature): add a correctly-rounded `FBig::log2` / `Context::log2` / `CachedFBig::log2`, computed as `ln(x)/ln(2)` at an elevated working precision. Previously only the f32-precision `log2_bounds` estimate existed, so directed log2 was wrong by many ULPs. The result magnitude tracks the division's error amplification, so a few guard digits certify the final round across the whole range. Each fix includes a regression test; the DASHU-008 test reproduces the exact memory.rs panic under the old code. Co-Authored-By: Claude <noreply@anthropic.com>
|
Some admittedly robotic feedback that is nonetheless helpful:
|
Reviewed the opendp-num differential/property-fuzz findings against the current source (
~/opendp-num/findings). Of the nine dashu findings:convert_base_odd/significand_bits+ the subnormal-halfway and high-precision regression tests are present).TODOcomments at theexp_internaloverflow branch andContext::powi. Fixing properly needs mode-aware range saturation — out of scope here.DASHU-015 (dashu-int) —
to_f64reports inexact asExactto_f64_smallused a saturatingf as DoubleWordround-trip for the exactness test; atDoubleWord::MAXthe value rounds up to2^BITS, the cast clamps back toDoubleWord::MAX, and the comparison reportsEqual→ wrongly taggedExact. Detect the saturation case first. Same guard applied to the 16/32-bitRefLarge→ f64 path.DASHU-026 (dashu-float) —
round_fractdebug assert OOMsThe debug assert built
B^precision. For a sparse sticky tail (whereprecisionis the exponent gap — reached viaexp_m1of a large-magnitude input) this allocated gigabytes in debug builds. Replaced with alog2_bounds-based precondition check that allocates nothing and only fires on a proven violation.DASHU-022 (dashu-float) — exact results panic under unlimited precision
assert_limited_precisionran before the exact zero/one shortcuts, so precision-0 values (FBig::try_from(0.0),FBig::ONE/ZERO) panicked inexp/exp_m1/sqrt/ln/ln_1pdespite exact results. Shortcuts hoisted above the assertion.DASHU-008 (dashu-int) — GCD scratchpad under-allocation
GCD reserved scratch once from the initial operand lengths, but each euclidean step dispatches on the current lengths, so a later lopsided Burnikel-Ziegler step was under-reserved →
"internal error: not enough memory allocated"(runtime.expect, so debug and release). Now sized for the loop-wide worst case:mul::memory_requirement_up_to(rhs_len, rhs_len / 2)(independent oflhs_len; returnszero_layoutfor small operands automatically). Applies to both the gcd and extended-gcd reservations.DASHU-007 (dashu-float) — add correctly-rounded
log2Previously only the f32-precision
log2_boundsestimate existed, so directedlog2was wrong by many ULPs. AddedFBig::log2/Context::log2/CachedFBig::log2computed asln(x)/ln(2)at an elevated working precision. The result magnitude tracks the division's error amplification, so a few guard digits certify the final round across the whole range.log2(2^k) = k;log2(1) = 0is certifiedExactvia the shortcut.Verification
cargo test --workspace --exclude dashu-python— all pass (incl. new regression tests; 85 float doctests)cargo clippy --all-features --all-targets --workspace --exclude dashu-python -- -D warnings— cleancargo fmt --all -- --check— cleanmemory.rs:150panic; the DASHU-026 regression (exp_m1(-2^62), which under the old assert would try to allocate ~8×10¹⁷ bits) completes in 0.00 s.## Unreleased) fordashu-int,dashu-float,dashu-ratio.🤖 Generated with Claude Code