You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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:
LowestSignificantDigits(...) for a non-integer power. This is uncapped, so it can be up to the 17 digits the double prints.
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.
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:
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
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.
What's wrong
Pow(SignificantNumber/SignificantNumber.cs~L600-611) andExp(~L630-637) compute the result asMath.Exp(Math.Log(|x|) * p)/Math.Exp(p)indouble. They then label it with:LowestSignificantDigits(...)for a non-integer power. This is uncapped, so it can be up to the 17 digits the double prints.int.Min(..., DoubleSignificantDigits), i.e. 15, for an integer power (the Pow with an integer exponent (and Exp) truncates the result to 1 significant digit: 1.234^2 == 2 #103/Keep the base's precision when Pow or Exp takes an integer exponent #104 fix).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'sSignificantDigitsclaims 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
decimalat 40–50 digits.Exp(700.12345678901234567890)Exp(-700.12345678901234567890)3.0000000000000000001.Pow(200.50000000000000000001)3.0000000000000000001.Pow(2.5000000000000000001)1.2345678901234567890.Pow(2000)(integer path, capped at 15)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.IsNormalbranch, and stay uncapped. #106 is about overflow and underflow, not about wrong digits inside the range.Suggested fix
Either:
DoubleSignificantDigits - ceil(log10(max(1, |p·ln|x||))), applied in one shared helper used by both the integer and non-integer paths ofPowandExp, and by Compute Pow and Exp beyond the range of a double [patch] #112's fallback. OrExactIntegerPower(forExp, use the 150-digitE), compute only x^f / e^f in double, then multiply and cap at 15 digits.Acceptance criteria
SignificantDigitsmatches the true value.Exp(±700.123…)and a large non-integerPow.