Skip to content

Compute Pow and Exp beyond the range of a double [patch] - #112

Merged
matt-edmondson merged 3 commits into
mainfrom
fix/pow-exp-beyond-double-range
Sep 28, 2026
Merged

matt-edmondson merged 3 commits into
mainfrom
fix/pow-exp-beyond-double-range

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Fixes #106

What was wrong

Outside the exact integer path, Pow and Exp computed through double:

  • A result above double's range threw ArgumentOutOfRangeException("Infinite values are not supported"). This hit 10.Pow(2000), Exp(710), and also 1e400.Pow(0.5), where the base itself converts to Infinity.
  • A result below double's range silently became an exact 0. This hit 2.Pow(-1100), 1e-400.Pow(0.5) and Exp(-1000).

Fix

  • The existing double computation still runs first. When its result is a normal double, nothing changes, so every in-range result is bit-for-bit what it was.
  • When the result isn't normal (infinite, zero or subnormal), work in base-10 logarithms instead:
    • log10|x| comes from BigInteger.Log10(significand) + exponent, so a base outside double's range works too.
    • For Exp, log10(e^p) = p·log10(e).
    • The result is rebuilt as 10^fraction × 10^whole with the whole power kept as an int (CreateFromComponents), so the exponent never passes through a double.
  • The result is rounded to the fewer of:
    • the usual significant-figure count
    • the digits the logarithm can actually determine, about 15 minus the digits in the integer part of the log. For Exp(±1000) that gives 12 digits, all of them correct.
  • A power of ten beyond ±1e9 throws OverflowException naming the operation, instead of returning infinity or zero. The Pow/Exp docs list the new exception.

Tests

The new SignificantNumberRangeTests covers every call in the issue's table, plus:

  • negative bases with odd powers in both directions
  • 1e400.Pow(-0.5)
  • checks of Exp(1000) and Exp(-1000) to 12 digits against high-precision reference values
  • OverflowException for extreme exponents in both directions

One correction to the issue: 1.5.Pow(2000) is 1.52e352, not ≈1.4e352, and the test expects 1.5E352.

With the fix reverted, all 12 new tests fail. With it, the suite passes, and the full solution builds clean for net7.0 through net10.0. This branch merges cleanly with #110 and #111.

🤖 Generated with Claude Code

https://claude.ai/code/session_01QBrEwaLGeo2SvjVK3AnLrp


Generated by Claude Code

Pow and Exp computed through double whenever they left the exact integer
path, so a result above double's range threw "Infinite values are not
supported" and one below it silently became an exact zero. When the double
result isn't a normal number, work in base-10 logarithms built from the
base's significand and exponent instead, and rebuild the result with its
power of ten kept as an integer. The result is rounded to the digits the
logarithm can determine, and a power of ten beyond ±1e9 throws
OverflowException rather than returning infinity or zero.

Fixes #106

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QBrEwaLGeo2SvjVK3AnLrp
Comment thread SignificantNumber/SignificantNumber.cs Fixed
@sonarqubecloud

Copy link
Copy Markdown

@matt-edmondson
matt-edmondson merged commit c53108e into main Sep 28, 2026
12 checks passed
@matt-edmondson
matt-edmondson deleted the fix/pow-exp-beyond-double-range branch September 28, 2026 01:47
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.

Pow and Exp throw for results beyond double range and return exact 0 for tiny ones (10.Pow(2000), Exp(710), 2.Pow(-1100))

2 participants