Skip to content

Take the logarithm of a subnormal base from its significand and exponent [patch] - #142

Merged
matt-edmondson merged 1 commit into
mainfrom
fix/140-subnormal-base-pow
Nov 4, 2026
Merged

matt-edmondson merged 1 commit into
mainfrom
fix/140-subnormal-base-pow

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Fixes #140

What was wrong

PowByLogarithm computed ln|x| from value.To<double>(). A base between about 4.9e-324 and 2.2e-308 converts to a subnormal double, which carries only a few significant digits. The fallback to the exact base-10 path only checked whether the result was a normal double, not the base. So when a fractional power brought the result back into normal range, the corrupted logarithm was used and the result still reported full precision:

Expression Before After Reference (Python decimal)
1.23456789e-320 ^ 0.5000001 1.111075e-160 1.111029e-160 1.11102926…e-160
1.23456789e-320 ^ -0.5000001 9.000291e159 9.000663e159 9.00066302…e159

Change

  • When the base is not a normal double, PowByLogarithm builds ln|x| from Log10OfMagnitude(value) * ln 10. That reads the significand and exponent directly, as the out-of-range fallback already does.
  • Normal-range bases keep Math.Log, so their results are unchanged. A base beyond double.MaxValue now takes the same route. It used to go through the fallback, which gave the same answer.

Tests

  • Added Pow_SubnormalDoubleBase_ReportsOnlyCorrectDigits with both acceptance-criteria cases. It checks that 7 digits are reported and that every one of them matches a 60-digit reference.
  • Both cases fail without the fix (9000291… vs expected 9000663…) and pass with it.
  • Full suite: 192/192 passing on net10.0.

Generated by Claude Code

…ent [patch]

PowByLogarithm took ln|x| through value.To<double>(). A base between about
4.9e-324 and 2.2e-308 becomes a subnormal double with only a few significant
digits, so a fractional power that brings the result back into normal range
was wrong from the 4th or 5th digit while still reporting 7 significant
digits: 1.23456789e-320 ^ 0.5000001 gave 1.111075e-160, not 1.111029e-160.

When the base is not a normal double, build ln|x| from Log10OfMagnitude, which
reads the significand and exponent and never passes the base through a double.
Normal bases keep Math.Log, so their results are unchanged.

Fixes #140

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

sonarqubecloud Bot commented Nov 3, 2026

Copy link
Copy Markdown

@matt-edmondson
matt-edmondson merged commit 2c8e05d into main Nov 4, 2026
15 checks passed
@matt-edmondson
matt-edmondson deleted the fix/140-subnormal-base-pow branch November 4, 2026 01:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants