Skip to content

Keep the base's precision when Pow or Exp takes an integer exponent - #104

Merged
matt-edmondson merged 1 commit into
mainfrom
fix/103-integer-pow-precision
Sep 26, 2026
Merged

matt-edmondson merged 1 commit into
mainfrom
fix/103-integer-pow-precision

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Fixes #103

What was wrong

Pow and Exp set their precision with LowestSignificantDigits(base, power). An integer exponent like 2 has 1 significant digit, so the result was rounded to 1 digit:

Expression Before After
1.234.Pow(2) 2 1.523 (equals 1.234 * 1.234)
1.234.Pow(3) 2 1.879 (equals 1.234 * 1.234 * 1.234)
(-2.5).Pow(3) -20 -16 (−15.625 to 2 digits)
1.234.Pow(-2) 0.7 0.6567
Exp(2) 7 7.38905609893065

Change

  • Pow with an integer exponent up to 1024: the exponent is treated as an exact count, like a counting number in a multiplication.
    • The result is computed by exact repeated squaring of the PreciseNumber. A negative power takes the reciprocal.
    • It is then rounded to the base's significant digits, so x.Pow(2) == x * x. This path also keeps the sign of a negative base, as the triage suggested.
  • Pow with a larger integer exponent: it still uses the log/exp path so that it can't build a huge intermediate. It keeps the base's digits, capped at 15 because the value is computed in a double.
  • Exp with an integer exponent: keeps 15 significant digits (E is capped to the double it is computed in).
  • Non-integer exponents: behave as before.

The commit carries [patch], matching #102. Results only gain precision; nothing that returned a value now throws.

Tests

New tests in SignificantNumberTests:

  • Pow_IntegerExponent_MatchesRepeatedMultiplication checks x.Pow(2) == x * x and x.Pow(3) == x * x * x for x = 1.234, including the exact values and the digit count.
  • Pow_NegativeBaseAndOddExponent_KeepsBasePrecision checks (-2.5)^3 → -16.
  • Pow_NegativeIntegerExponent_MatchesReciprocalOfPower checks 1.234^-2 → 0.6567.
  • Exp_IntegerExponent_KeepsMoreThanOneSignificantDigit checks that Exp(2) has more than 1 digit and matches Math.Exp(2).

Verification

  • Against main, all 4 new tests fail (2, -20, 0.7 and 1 digit).
  • With the fix, 103 of 103 tests pass, the existing 3^2, 2^3 and (-2)^3 cases included.
  • The Release build has no warnings on any target framework.

🤖 Generated with Claude Code

https://claude.ai/code/session_01NvZoJBSka1emZMaEXy4Cfj


Generated by Claude Code

…patch]

An integer exponent carried its own single significant digit into the result's
precision, so 1.234.Pow(2) returned 2 and Exp(2) returned 7. An integer power
is now treated as an exact count, like a counting number in a multiplication:

- Pow computes integer powers up to 1024 by exact repeated squaring (a
  reciprocal for a negative power) and keeps the base's significant digits,
  so x.Pow(2) equals x * x. Larger integer powers keep the base's digits,
  capped at what the double they are computed in can hold.
- Exp with an integer exponent keeps up to 15 significant digits, the
  precision of the double it is computed in.

Fixes #103

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NvZoJBSka1emZMaEXy4Cfj
@sonarqubecloud

Copy link
Copy Markdown

@matt-edmondson
matt-edmondson merged commit 21582d1 into main Sep 26, 2026
12 checks passed
@matt-edmondson
matt-edmondson deleted the fix/103-integer-pow-precision branch September 26, 2026 23: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 with an integer exponent (and Exp) truncates the result to 1 significant digit: 1.234^2 == 2

2 participants