github.com/deso-protocol/core@v1.2.9/lib/deso_math.go (about) 1 package lib 2 3 import ( 4 "fmt" 5 "math/big" 6 ) 7 8 // This library implements basic float functions using big.Float objects. 9 // This is necessary in order to ensure interoperability across different 10 // machines. If we instead were to use float64's for our computations 11 // naively, then machines with different rounding rules or different 12 // precision for intermediate values could produce different results that 13 // would cause blockchain forks. Having our own library ensures not only 14 // that such forks can't occur but also makes it so that implementing a 15 // node in another language is fairly straightforward because all of the 16 // operations are implemented in software. 17 18 const ( 19 FloatPrecision uint = 53 20 ) 21 22 func NewFloat() *big.Float { 23 // We force all calculations be done at a particular precision. This keeps 24 // all nodes in sync and avoids consensus issues around one node using a 25 // different precision than another node. We also force the same rounding 26 // mode for all calculations. 27 return big.NewFloat(0.0).SetPrec(FloatPrecision).SetMode(big.ToNearestEven) 28 } 29 30 func IntSub(a *big.Int, b *big.Int) *big.Int { 31 // TODO(performance): We should do this without creating an int copy, but 32 // this is easier to understand and deal with for now. 33 return big.NewInt(0).Sub(a, b) 34 } 35 36 func IntMul(a *big.Int, b *big.Int) *big.Int { 37 // TODO(performance): We should do this without creating an int copy, but 38 // this is easier to understand and deal with for now. 39 return big.NewInt(0).Mul(a, b) 40 } 41 42 func IntDiv(a *big.Int, b *big.Int) *big.Int { 43 // TODO(performance): We should do this without creating an int copy, but 44 // this is easier to understand and deal with for now. 45 return big.NewInt(0).Quo(a, b) 46 } 47 48 func IntAdd(a *big.Int, b *big.Int) *big.Int { 49 // TODO(performance): We should do this without creating an int copy, but 50 // this is easier to understand and deal with for now. 51 return big.NewInt(0).Add(a, b) 52 } 53 54 func Sub(a *big.Float, b *big.Float) *big.Float { 55 // TODO(performance): This code currently calls NewFloat() too often. It 56 // does this in order to make the code easier to read but if it ever becomes 57 // an issue, the superfluous calls to NewFloat() should be a quick win. 58 return NewFloat().Sub(a, b) 59 } 60 61 func Mul(a *big.Float, b *big.Float) *big.Float { 62 // TODO(performance): This code currently calls NewFloat() too often. It 63 // does this in order to make the code easier to read but if it ever becomes 64 // an issue, the superfluous calls to NewFloat() should be a quick win. 65 return NewFloat().Mul(a, b) 66 } 67 68 func Div(a *big.Float, b *big.Float) *big.Float { 69 // TODO(performance): This code currently calls NewFloat() too often. It 70 // does this in order to make the code easier to read but if it ever becomes 71 // an issue, the superfluous calls to NewFloat() should be a quick win. 72 return NewFloat().Quo(a, b) 73 } 74 75 func Add(a *big.Float, b *big.Float) *big.Float { 76 // TODO(performance): This code currently calls NewFloat() too often. It 77 // does this in order to make the code easier to read but if it ever becomes 78 // an issue, the superfluous calls to NewFloat() should be a quick win. 79 return NewFloat().Add(a, b) 80 } 81 82 var ( 83 // Constants for BigFloatLog 84 bigLn2Hi = NewFloat().SetFloat64(6.93147180369123816490e-01) /* 3fe62e42 fee00000 */ 85 bigLn2Lo = NewFloat().SetFloat64(1.90821492927058770002e-10) /* 3dea39ef 35793c76 */ 86 bigL1 = NewFloat().SetFloat64(6.666666666666735130e-01) /* 3FE55555 55555593 */ 87 bigL2 = NewFloat().SetFloat64(3.999999999940941908e-01) /* 3FD99999 9997FA04 */ 88 bigL3 = NewFloat().SetFloat64(2.857142874366239149e-01) /* 3FD24924 94229359 */ 89 bigL4 = NewFloat().SetFloat64(2.222219843214978396e-01) /* 3FCC71C5 1D8E78AF */ 90 bigL5 = NewFloat().SetFloat64(1.818357216161805012e-01) /* 3FC74664 96CB03DE */ 91 bigL6 = NewFloat().SetFloat64(1.531383769920937332e-01) /* 3FC39A09 D078C69F */ 92 bigL7 = NewFloat().SetFloat64(1.479819860511658591e-01) /* 3FC2F112 DF3E5244 */ 93 bigSqrt2 = NewFloat().SetFloat64(1.41421356237309504880168872420969807856967187537694807317667974) 94 bigHalf = NewFloat().SetFloat64(.5) 95 bigNegativeOneOne = NewFloat().SetUint64(1) 96 bigOne = NewFloat().SetUint64(1) 97 bigTwo = NewFloat().SetUint64(2) 98 bigSqrt2Over2 = NewFloat().Quo(bigSqrt2, bigTwo) 99 100 // Constants for BigFloatExpMulti 101 bigP1 = NewFloat().SetFloat64(1.66666666666666657415e-01) /* 0x3FC55555; 0x55555555 */ 102 bigP2 = NewFloat().SetFloat64(-2.77777777770155933842e-03) /* 0xBF66C16C; 0x16BEBD93 */ 103 bigP3 = NewFloat().SetFloat64(6.61375632143793436117e-05) /* 0x3F11566A; 0xAF25DE2C */ 104 bigP4 = NewFloat().SetFloat64(-1.65339022054652515390e-06) /* 0xBEBBBD41; 0xC5D26BF1 */ 105 bigP5 = NewFloat().SetFloat64(4.13813679705723846039e-08) /* 0x3E663769; 0x72BEA4D0 */ 106 107 // Constants for BigFloatExp 108 bigZero = NewFloat().SetUint64(0) 109 bigLog2e = NewFloat().SetFloat64(1.44269504088896338700e+00) 110 ) 111 112 // Log returns the natural logarithm of x. 113 func BigFloatLog(x *big.Float) *big.Float { 114 // special cases 115 // TODO: We should make the special cases work at some point. 116 switch { 117 case x.IsInf(): 118 panic(fmt.Sprintf("BigFloatLog: Cannot take log of an infinite number: %v", x)) 119 case x.Sign() <= 0: 120 panic(fmt.Sprintf("BigFloatLog: Cannot take log of a number <= 0: %v", x)) 121 } 122 123 // Reduce 124 f1 := NewFloat() 125 ki := x.MantExp(f1) 126 if f1.Cmp(bigSqrt2Over2) < 0 { 127 f1 = Mul(f1, bigTwo) 128 ki-- 129 } 130 f := Sub(f1, bigOne) 131 k := NewFloat().SetInt64(int64(ki)) 132 133 // Compute 134 twoPlusF := Add(bigTwo, f) 135 s := Div(f, twoPlusF) 136 s2 := Mul(s, s) 137 s4 := Mul(s2, s2) 138 139 t1 := Mul(s2, (Add(bigL1, Mul(s4, (Add(bigL3, Mul(s4, (Add(bigL5, Mul(s4, bigL7)))))))))) 140 t2 := Mul(s4, Add(bigL2, Mul(s4, Add(bigL4, Mul(s4, bigL6))))) 141 R := Add(t1, t2) 142 hfsq := Mul(bigHalf, NewFloat().Mul(f, f)) 143 144 return Sub(Mul(k, bigLn2Hi), Sub(Sub(hfsq, (Add(Mul(s, Add(hfsq, R)), Mul(k, bigLn2Lo)))), f)) 145 } 146 147 // Log2 returns the binary logarithm of x. 148 // The special cases are the same as for Log. 149 func BigFloatLog2(x *big.Float) *big.Float { 150 bigTwo := NewFloat().SetUint64(2) 151 return Div(BigFloatLog(x), BigFloatLog(bigTwo)) 152 } 153 154 func BigFloatExpMulti(hi, lo *big.Float, k int64) *big.Float { 155 r := Sub(hi, lo) 156 t := Mul(r, r) 157 c := Sub(r, Mul(t, (Add(bigP1, Mul(t, (Add(bigP2, Mul(t, (Add(bigP3, Mul(t, (Add(bigP4, Mul(t, bigP5)))))))))))))) 158 y := Sub(bigOne, (Sub((Sub(lo, Div((Mul(r, c)), (Sub(bigTwo, c))))), hi))) 159 160 // TODO: make sure Ldexp can handle boundary k 161 return NewFloat().SetMantExp(y, int(k)) 162 } 163 164 // Exp returns a big.Float representation of exp(z). 165 func BigFloatExp(z *big.Float) *big.Float { 166 if z.IsInf() { 167 panic("BigFloatExp: Cannot call exp with infinity") 168 } 169 170 // reduce; computed as r = hi - lo for extra precision. 171 var k int64 172 switch { 173 case z.Cmp(bigZero) < 0: 174 k, _ = Sub(Mul(bigLog2e, z), bigHalf).Int64() 175 case z.Cmp(bigZero) > 0: 176 k, _ = Add(Mul(bigLog2e, z), bigHalf).Int64() 177 } 178 hi := Sub(z, Mul(NewFloat().SetInt64(k), bigLn2Hi)) 179 lo := Mul(NewFloat().SetInt64(k), bigLn2Lo) 180 181 // compute 182 return BigFloatExpMulti(hi, lo, k) 183 } 184 185 // Pow returns a big.Float representation of z**w. 186 func BigFloatPow(z *big.Float, w *big.Float) *big.Float { 187 if z.Sign() < 0 { 188 panic("Pow: negative base") 189 } 190 if z.Cmp(bigZero) == 0 { 191 return bigZero 192 } 193 194 // Pow(z, 0) = 1.0 195 if w.Sign() == 0 { 196 return bigOne 197 } 198 199 // Pow(z, 1) = z 200 // Pow(+Inf, n) = +Inf 201 if w.Cmp(bigOne) == 0 || z.IsInf() { 202 return NewFloat().Copy(z) 203 } 204 205 // Pow(z, -w) = 1 / Pow(z, w) 206 if w.Sign() < 0 { 207 x := NewFloat() 208 zExt := NewFloat().Copy(z).SetPrec(z.Prec() + 64) 209 wNeg := NewFloat().Neg(w) 210 return x.Quo(bigOne, BigFloatPow(zExt, wNeg)).SetPrec(z.Prec()) 211 } 212 213 // compute w**z as exp(z log(w)) 214 x := NewFloat().SetPrec(z.Prec() + 64) 215 logZ := BigFloatLog(NewFloat().Copy(z).SetPrec(z.Prec() + 64)) 216 x.Mul(w, logZ) 217 x = BigFloatExp(x) 218 return x.SetPrec(z.Prec()) 219 }