What's wrong
PreciseNumber/PreciseNumber.Exponentials.cs, in Exp2(x, significantDigits):
return ExpOfProductWithConstant(x, Ln2To(significantDigits + ExponentialGuardDigits), significantDigits);
This reads ln 2 at a fixed sd + 10 digits. Multiplying by x scales the absolute error in ln 2 by |x|, and that error becomes the relative error of 2^x. Once x has about 10 integer digits, the guard digits are gone and the digit that the final rounding decision depends on is wrong.
The sibling functions already account for this:
Exp2M1 (line ~562): Ln2To(working + IntegerDigitCount(x))
Exp10M1 does the same with ln 10
Exp and FractionalPow widen by the digits they consume
Reproduction
These compare Exp2(x, d) with Exp2(x, d + 40).ReduceSignificance(d). Exp(x·ln2, d + 40) agrees with the second value.
| Call |
Actual significand |
Expected significand |
Exp2(7000000000.5, 3) |
628e2107209967 |
629e2107209967 |
Exp2(-6000000000.5, 5) |
73384e-1806179979 |
73383e-1806179979 |
Integer x takes an exact path, so only non-integer x is affected.
Suggested fix
Match Exp2M1:
return ExpOfProductWithConstant(x, Ln2To(significantDigits + ExponentialGuardDigits + IntegerDigitCount(x)), significantDigits);
Check Exp10 for the same pattern with ln 10.
Acceptance: both rows above return the expected significand, with tests. The tests should compare significands or use ReduceSignificance, because ToString on these exponents runs out of memory.
What's wrong
PreciseNumber/PreciseNumber.Exponentials.cs, inExp2(x, significantDigits):This reads ln 2 at a fixed
sd + 10digits. Multiplying byxscales the absolute error in ln 2 by |x|, and that error becomes the relative error of 2^x. Oncexhas about 10 integer digits, the guard digits are gone and the digit that the final rounding decision depends on is wrong.The sibling functions already account for this:
Exp2M1(line ~562):Ln2To(working + IntegerDigitCount(x))Exp10M1does the same with ln 10ExpandFractionalPowwiden by the digits they consumeReproduction
These compare
Exp2(x, d)withExp2(x, d + 40).ReduceSignificance(d).Exp(x·ln2, d + 40)agrees with the second value.Exp2(7000000000.5, 3)628e2107209967629e2107209967Exp2(-6000000000.5, 5)73384e-180617997973383e-1806179979Integer
xtakes an exact path, so only non-integerxis affected.Suggested fix
Match
Exp2M1:Check
Exp10for the same pattern with ln 10.Acceptance: both rows above return the expected significand, with tests. The tests should compare significands or use
ReduceSignificance, becauseToStringon these exponents runs out of memory.