Skip to content

Fix powi spurious overflow for bases near 1 on 32-bit (#95)#96

Merged
cmpute merged 1 commit into
masterfrom
worktree-issue95
Jul 25, 2026
Merged

Fix powi spurious overflow for bases near 1 on 32-bit (#95)#96
cmpute merged 1 commit into
masterfrom
worktree-issue95

Conversation

@cmpute

@cmpute cmpute commented Jul 24, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes #95 — high-precision base-2 → base-10 conversion (FBig::with_base) panicked on 32-bit targets (wasm32, i686) with arithmetic operations with the infinity are not allowed!.

Root cause

with_base evaluates exp(r) internally as sum^(B^n) via Context::powi with a huge integer exponent B^n on a base (sum, the Maclaurin-series result) that is very close to 1.

powi's overflow guard estimated log2(base) with log2_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 of f32 noise instead of ~0. Scaled by the large exponent, that noise crossed the overflow threshold — which on 32-bit is only isize::MAX·log2(B) ≈ 7e9 (vs ≈3e19 on 64-bit), so it only manifested on 32-bit:

base_log2 = 1.22e-4   (true log2(sum) ≈ 3e-17)
exp_f64 * base_log2 = 1e16 * 1.22e-4 = 1.22e12  >  threshold 7.13e9   → spurious Overflow

powi returned ±inf, and the subsequent v << s panicked (shifting an infinity).

Fix

Switch the guard to the bit-length-based log2_bounds, which doesn't cancel:

  • declare overflow only on the lower bound of log2(base) (no false positives),
  • declare underflow only on the upper bound (no false underflows, and excluded for a zero base),
  • anything in between (e.g. a base that straddles 1) is computed.

Verification

Reproduced on real 32-bit (i686-unknown-linux-gnu, usize::BITS = 32) before the fix, confirmed gone after:

  • Reporter's exact input (precision 586) now converts to the same value as 64-bit.
  • New regression test convert::tests::with_base_high_precision_no_overflow reliably fails on the buggy code and passes on the fix (verified on i686).
  • dashu-float tests pass on i686 (77) and x86_64 (83); workspace tests + clippy (-D warnings) + fmt --check + cargo check --all-features --tests all green.

The crash was codegen-sensitive (it depended on how LLVM folded the cancelling f32 ops, so it surfaced via the dashu meta-crate / default features but not a bare no_std build) — the fix is at the algorithm level, so it's robust to all of those.

🤖 Generated with Claude Code

@cmpute cmpute changed the title Fix spurious overflow for bases near 1 on 32-bit (#95) Fix powi spurious overflow for bases near 1 on 32-bit (#95) Jul 24, 2026
@cmpute
cmpute force-pushed the worktree-issue95 branch from 400c3c2 to e388ca4 Compare July 24, 2026 16:31
`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>
@cmpute
cmpute force-pushed the worktree-issue95 branch from e388ca4 to 4dee7e6 Compare July 25, 2026 05:56
@cmpute
cmpute merged commit ea02450 into master Jul 25, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Crash when converting base-2 to base-10 floating-point number with manually set higher precision on 32-bit Windows targets

1 participant