Repository navigation
Conversation
ExpTaylor summed Taylor terms that were each rounded to one guard digit, and for negative d rounded 1/e^|d| twice, so about 1 in 10 results had a wrong last digit, like ExpTaylor(0.0003, 11) = 1.00030004501 instead of 1.00030004500 and ExpTaylor(-3, 1) = 0.1 instead of 0. It now evaluates e^d = 10^q * e^s with the expFixed and ln10Fixed helpers that Pow and Ln use, and raises the working precision until the whole error interval rounds the same way. The lower half of powFracFixed moves into expBounds so both share it. Results too large to represent return an error instead of running out of memory.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes
ExpTaylorreturning wrong last digits, likeExpTaylor(0.0003, 11)giving1.00030004501instead of1.00030004500(e^0.0003 = 1.000300045004500...). It is the same problem #471 fixed forLn.Each Taylor term was rounded to
precision + 1places before being added, so the result had only one guard digit and the rounding errors of all terms added up. Negative inputs were rounded twice, firste^|d|and then1/e^|d|. That is why the existing test expectedExpTaylor(-3, 1)to be0.1. e^-3 is 0.0498, so the right answer is0, and this PR changes that expectation.ExpTaylornow writes e^d = 10^q * e^s, with d = q*ln(10) + s and 0 <= s < ln(10). It computes this with theexpFixed/ln10Fixedhelpers thatPowandLnalready use, and raises the working precision until the whole error interval rounds the same way. The lower half ofpowFracFixedalready did exactly this forPow, so it moves into a sharedexpBoundshelper. Pow's results are unchanged: 2,326 randomPowWithPrecisioncases give byte-identical output before and after.BenchmarkDecimal_ExpTaylorgoes from 111 µs to 1.5 µs. Large inputs gain the most. 274 random cases with 0 < d < 1490 took 47 s on master and 0.02 s here.ExpTaylorcould only return a nil error before. It now returns the same out-of-range error asPowwhen the result or the precision is too large to represent, for example1e10, where master had no result after 30 s. Results that round to 0 still return 0, for example-1e5and-1e10, where master also had no result after 30 s.ExpTaylor(1e5, 2)now takes about 1 s.Behaviour change: some results change in the last digit, like
Lnin v1.6.0. All the other existingExpTaylortest expectations were already correctly rounded and still pass. Only-3.0at precision 1 changes, from0.1to0.New tests: last-digit cases checked against mpmath, two near-tie inputs where e^x is 2.5 - 2e-45 and 0.25 - 2e-46 (so the first working precisions can't decide the rounding), and out-of-range/underflow cases.
Checks run
go test .passes.go test -race -run 'ExpTaylor|Pow|Ln' .passes.golangci-lint run ./...(v2.13.2 locally, CI pins v2.14.0) reports 0 issues.gofmt -l .is clean.math.go,TestDecimal_ExpTaylorfails 7 cases, and the out-of-range test does not finish beforego test's 10-minute timeout. With the fix, everything passes.math.Ln10,big.Int.DivMod, the existing helpers).