From b01fb426de300c53618cccb5d4676e59b316ebe0 Mon Sep 17 00:00:00 2001 From: xusong Date: Fri, 11 Oct 2024 10:36:42 +0800 Subject: [PATCH] refactor:(ApproxRoot) refact ApproxRoot() for better read and performance --- math/dec.go | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/math/dec.go b/math/dec.go index 3ed930d5c484..e4ced97bef91 100644 --- a/math/dec.go +++ b/math/dec.go @@ -472,28 +472,44 @@ func (d LegacyDec) ApproxRoot(root uint64) (guess LegacyDec, err error) { return absRoot.NegMut(), err } - // One decimal, that we invalidate later. Helps us save a heap allocation. - scratchOneDec := LegacyOneDec() - if root == 1 || d.IsZero() || d.Equal(scratchOneDec) { + // Special cases + if root == 1 || d.IsZero() { return d, nil } + // Handle d == 1 case + if d.Equal(LegacyOneDec()) { + return LegacyOneDec(), nil + } + if root == 0 { - return scratchOneDec, nil + return LegacyOneDec(), nil } - guess, delta := scratchOneDec, LegacyOneDec() + // Initial guess (could improve with better heuristics) + guess = LegacyOneDec() // start with 1 + // Constants + rootDec := LegacyDecFromInt64(int64(root)) + rootMinusOne := root - 1 - for iter := 0; iter < maxApproxRootIterations && delta.Abs().GT(smallestDec); iter++ { - prev := guess.Power(root - 1) + // Iteratively apply Newton's method + for iter := 0; iter < maxApproxRootIterations; iter++ { + // Compute guess^(root-1) + prev := guess.Power(rootMinusOne) if prev.IsZero() { - prev = smallestDec + prev = smallestDec // avoid division by zero } - delta.Set(d).QuoMut(prev) - delta.SubMut(guess) - delta.QuoInt64Mut(int64(root)) + // delta = (d / guess^(root-1) - guess) / root + delta := d.Quo(prev).Sub(guess).Quo(rootDec) + + // Update guess: guess = guess + delta guess.AddMut(delta) + + // If delta is small enough, break early (convergence) + if delta.Abs().LT(smallestDec) { + break + } } return guess, nil