Fix powi spurious overflow for bases near 1 on 32-bit (#95)#96
Merged
Conversation
`powi`'s overflow guard estimated `log2(base)` with `log2_est`, which for a base
very close to 1 (a large significand with a large negative exponent) is the
difference of two ~1e3-magnitude terms and catastrophically cancels to ~1e-4 of
f32 noise. Scaled by a large exponent that noise crossed the overflow threshold —
which on 32-bit targets is only `isize::MAX·log2(B) ≈ 7e9` (vs ≈3e19 on 64-bit) —
so the guard fired spuriously and returned ±inf.
This crashed high-precision base conversion: `FBig::with_base` evaluates `exp(r)`
internally as `sum^(B^n)` through `powi` with a huge integer exponent `B^n` on a
base (`sum`) that is very close to 1. The spurious ±inf then panicked at
`v << s` ("arithmetic operations with the infinity are not allowed!") on wasm32
and i686.
Switch the guard to the bit-length-based `log2_bounds`: declare overflow only on
the lower bound of `log2(base)` (no false positives) and underflow only on the
upper bound (no false underflows); anything in between is computed. The bounds
are derived from the exact significand bit length, so they don't cancel.
Adds a regression test with the reporter's exact high-precision base-2 → base-10
conversion, which reliably reproduced the panic on 32-bit.
Fixes #95.
Co-Authored-By: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #95 — high-precision base-2 → base-10 conversion (
FBig::with_base) panicked on 32-bit targets (wasm32, i686) witharithmetic operations with the infinity are not allowed!.Root cause
with_baseevaluatesexp(r)internally assum^(B^n)viaContext::powiwith a huge integer exponentB^non a base (sum, the Maclaurin-series result) that is very close to 1.powi's overflow guard estimatedlog2(base)withlog2_est. For a base ≈ 1 (a large significand with a large negative exponent),log2(base)is the difference of two ~1e3-magnitude terms and catastrophically cancels to ~1e-4 off32noise instead of ~0. Scaled by the large exponent, that noise crossed the overflow threshold — which on 32-bit is onlyisize::MAX·log2(B) ≈ 7e9(vs ≈3e19 on 64-bit), so it only manifested on 32-bit:powireturned±inf, and the subsequentv << spanicked (shifting an infinity).Fix
Switch the guard to the bit-length-based
log2_bounds, which doesn't cancel:log2(base)(no false positives),Verification
Reproduced on real 32-bit (
i686-unknown-linux-gnu,usize::BITS = 32) before the fix, confirmed gone after:convert::tests::with_base_high_precision_no_overflowreliably fails on the buggy code and passes on the fix (verified on i686).dashu-floattests pass on i686 (77) and x86_64 (83); workspace tests + clippy (-D warnings) +fmt --check+cargo check --all-features --testsall green.The crash was codegen-sensitive (it depended on how LLVM folded the cancelling
f32ops, so it surfaced via thedashumeta-crate / default features but not a bareno_stdbuild) — the fix is at the algorithm level, so it's robust to all of those.🤖 Generated with Claude Code