github.com/filecoin-project/specs-actors/v4@v4.0.2/actors/util/math/ln.go (about)

     1  package math
     2  
     3  import (
     4  	gbig "math/big"
     5  
     6  	"github.com/filecoin-project/go-state-types/big"
     7  )
     8  
     9  var (
    10  	// Coefficients in Q.128 format
    11  	lnNumCoef   []*gbig.Int
    12  	lnDenomCoef []*gbig.Int
    13  	ln2         big.Int
    14  )
    15  
    16  func init() {
    17  	// ln approximation coefficients
    18  	// parameters are in integer format,
    19  	// coefficients are *2^-128 of that
    20  	// so we can just load them if we treat them as Q.128
    21  	num := []string{
    22  		"261417938209272870992496419296200268025",
    23  		"7266615505142943436908456158054846846897",
    24  		"32458783941900493142649393804518050491988",
    25  		"17078670566130897220338060387082146864806",
    26  		"-35150353308172866634071793531642638290419",
    27  		"-20351202052858059355702509232125230498980",
    28  		"-1563932590352680681114104005183375350999",
    29  	}
    30  	lnNumCoef = Parse(num)
    31  
    32  	denom := []string{
    33  		"49928077726659937662124949977867279384",
    34  		"2508163877009111928787629628566491583994",
    35  		"21757751789594546643737445330202599887121",
    36  		"53400635271583923415775576342898617051826",
    37  		"41248834748603606604000911015235164348839",
    38  		"9015227820322455780436733526367238305537",
    39  		"340282366920938463463374607431768211456",
    40  	}
    41  	lnDenomCoef = Parse(denom)
    42  
    43  	constStrs := []string{
    44  		"235865763225513294137944142764154484399", // ln(2)
    45  	}
    46  	constBigs := Parse(constStrs)
    47  	ln2 = big.NewFromGo(constBigs[0])
    48  }
    49  
    50  // The natural log of Q.128 x.
    51  func Ln(z big.Int) big.Int {
    52  	// bitlen - 1 - precision
    53  	k := int64(z.BitLen()) - 1 - Precision128 // Q.0
    54  	x := big.Zero()                           // nolint:ineffassign
    55  
    56  	if k > 0 {
    57  		x = big.Rsh(z, uint(k)) // Q.128
    58  	} else {
    59  		x = big.Lsh(z, uint(-k)) // Q.128
    60  	}
    61  
    62  	// ln(z) = ln(x * 2^k) = ln(x) + k * ln2
    63  	lnz := big.Mul(big.NewInt(k), ln2)         // Q.0 * Q.128 => Q.128
    64  	return big.Sum(lnz, lnBetweenOneAndTwo(x)) // Q.128
    65  }
    66  
    67  // The natural log of x, specified in Q.128 format
    68  // Should only use with 1 <= x <= 2
    69  // Output is in Q.128 format.
    70  func lnBetweenOneAndTwo(x big.Int) big.Int {
    71  	// ln is approximated by rational function
    72  	// polynomials of the rational function are evaluated using Horner's method
    73  	num := Polyval(lnNumCoef, x.Int)     // Q.128
    74  	denom := Polyval(lnDenomCoef, x.Int) // Q.128
    75  
    76  	num = num.Lsh(num, Precision128)          // Q.128 => Q.256
    77  	return big.NewFromGo(num.Div(num, denom)) // Q.256 / Q.128 => Q.128
    78  }