Skip to content

Pow and Exp report 15–17 significant digits when only 12–13 are correct for large |power·ln x| #113

Description

@matt-edmondson

What's wrong

Pow (SignificantNumber/SignificantNumber.cs ~L600-611) and Exp (~L630-637) compute the result as Math.Exp(Math.Log(|x|) * p) / Math.Exp(p) in double. They then label it with:

Math.Exp(y) has a relative error of about |y|·2⁻⁵³, so when |p·ln x| is in the hundreds, 2–3 digits are wrong. The result's SignificantDigits claims precision it doesn't have, and that false precision carries into every later calculation. This breaks the library's central promise that the digits it reports are significant.

Failure scenario

Scratch MSTest on .NET 10. Reference values are from Python decimal at 40–50 digits.

Call Actual (reported sig digits) True value Correct digits
Exp(700.12345678901234567890) 1.1475032771154229e304 (17) 1.14750327711538352e304 12
Exp(-700.12345678901234567890) 8.714572062171235e-305 (16) 8.71457206217153310e-305 12
3.0000000000000000001.Pow(200.50000000000000000001) 4.6005692393405503e95 (17) 4.60056923934049687e95 13
3.0000000000000000001.Pow(2.5000000000000000001) 15.588457268119901 (17) 15.5884572681198956 15
1.2345678901234567890.Pow(2000) (integer path, capped at 15) 1.07140686654947e183 (15) 1.07140686654964739e183 13

Open PR #112 (for #106) caps digits only on its new out-of-range fallback. All of these results are inside double's range, take the double.IsNormal branch, and stay uncapped. #106 is about overflow and underflow, not about wrong digits inside the range.

Suggested fix

Either:

  1. Cap the digits: limit the reported precision to DoubleSignificantDigits - ceil(log10(max(1, |p·ln|x||))), applied in one shared helper used by both the integer and non-integer paths of Pow and Exp, and by Compute Pow and Exp beyond the range of a double [patch] #112's fallback. Or
  2. Compute more precisely: split p into an integer part n and a fraction f with |f| < 1. Compute xⁿ exactly with ExactIntegerPower (for Exp, use the 150-digit E), compute only x^f / e^f in double, then multiply and cap at 15 digits.

Acceptance criteria

  • For each row above, every digit of the returned value that falls within its reported SignificantDigits matches the true value.
  • A test covers Exp(±700.123…) and a large non-integer Pow.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingreadyFully specified; implement as written

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions