2018-08-14 17:15:02 -07:00
|
|
|
package types
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
2019-12-27 09:57:54 -08:00
|
|
|
"errors"
|
2018-08-14 17:15:02 -07:00
|
|
|
"fmt"
|
|
|
|
"math/big"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
)
|
|
|
|
|
2020-01-24 07:32:00 -08:00
|
|
|
var _ CustomProtobufType = (*Dec)(nil)
|
|
|
|
|
2018-08-14 17:15:02 -07:00
|
|
|
// NOTE: never use new(Dec) or else we will panic unmarshalling into the
|
|
|
|
// nil embedded big.Int
|
|
|
|
type Dec struct {
|
2020-01-24 07:32:00 -08:00
|
|
|
i *big.Int
|
2018-08-14 17:15:02 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
const (
|
2020-08-29 03:13:36 -07:00
|
|
|
// number of decimal places
|
2019-01-17 09:15:10 -08:00
|
|
|
Precision = 18
|
2018-08-14 17:15:02 -07:00
|
|
|
|
|
|
|
// bytes required to represent the above precision
|
2019-01-17 09:15:10 -08:00
|
|
|
// Ceiling[Log2[999 999 999 999 999 999]]
|
|
|
|
DecimalPrecisionBits = 60
|
2020-08-29 03:13:36 -07:00
|
|
|
|
2021-06-16 07:18:02 -07:00
|
|
|
maxDecBitLen = maxBitLen + DecimalPrecisionBits
|
|
|
|
|
2020-08-29 03:13:36 -07:00
|
|
|
// max number of iterations in ApproxRoot function
|
|
|
|
maxApproxRootIterations = 100
|
2018-08-14 17:15:02 -07:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
precisionReuse = new(big.Int).Exp(big.NewInt(10), big.NewInt(Precision), nil)
|
|
|
|
fivePrecision = new(big.Int).Quo(precisionReuse, big.NewInt(2))
|
|
|
|
precisionMultipliers []*big.Int
|
|
|
|
zeroInt = big.NewInt(0)
|
|
|
|
oneInt = big.NewInt(1)
|
|
|
|
tenInt = big.NewInt(10)
|
|
|
|
)
|
|
|
|
|
2019-12-27 09:57:54 -08:00
|
|
|
// Decimal errors
|
|
|
|
var (
|
|
|
|
ErrEmptyDecimalStr = errors.New("decimal string cannot be empty")
|
|
|
|
ErrInvalidDecimalLength = errors.New("invalid decimal length")
|
|
|
|
ErrInvalidDecimalStr = errors.New("invalid decimal string")
|
|
|
|
)
|
|
|
|
|
2018-08-14 17:15:02 -07:00
|
|
|
// Set precision multipliers
|
|
|
|
func init() {
|
|
|
|
precisionMultipliers = make([]*big.Int, Precision+1)
|
|
|
|
for i := 0; i <= Precision; i++ {
|
|
|
|
precisionMultipliers[i] = calcPrecisionMultiplier(int64(i))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func precisionInt() *big.Int {
|
|
|
|
return new(big.Int).Set(precisionReuse)
|
|
|
|
}
|
|
|
|
|
2019-05-24 18:23:53 -07:00
|
|
|
func ZeroDec() Dec { return Dec{new(big.Int).Set(zeroInt)} }
|
|
|
|
func OneDec() Dec { return Dec{precisionInt()} }
|
|
|
|
func SmallestDec() Dec { return Dec{new(big.Int).Set(oneInt)} }
|
2018-08-14 17:15:02 -07:00
|
|
|
|
|
|
|
// calculate the precision multiplier
|
|
|
|
func calcPrecisionMultiplier(prec int64) *big.Int {
|
|
|
|
if prec > Precision {
|
|
|
|
panic(fmt.Sprintf("too much precision, maximum %v, provided %v", Precision, prec))
|
|
|
|
}
|
|
|
|
zerosToAdd := Precision - prec
|
|
|
|
multiplier := new(big.Int).Exp(tenInt, big.NewInt(zerosToAdd), nil)
|
|
|
|
return multiplier
|
|
|
|
}
|
|
|
|
|
|
|
|
// get the precision multiplier, do not mutate result
|
|
|
|
func precisionMultiplier(prec int64) *big.Int {
|
|
|
|
if prec > Precision {
|
|
|
|
panic(fmt.Sprintf("too much precision, maximum %v, provided %v", Precision, prec))
|
|
|
|
}
|
|
|
|
return precisionMultipliers[prec]
|
|
|
|
}
|
|
|
|
|
|
|
|
// create a new Dec from integer assuming whole number
|
|
|
|
func NewDec(i int64) Dec {
|
|
|
|
return NewDecWithPrec(i, 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
// create a new Dec from integer with decimal place at prec
|
|
|
|
// CONTRACT: prec <= Precision
|
|
|
|
func NewDecWithPrec(i, prec int64) Dec {
|
|
|
|
return Dec{
|
|
|
|
new(big.Int).Mul(big.NewInt(i), precisionMultiplier(prec)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// create a new Dec from big integer assuming whole numbers
|
|
|
|
// CONTRACT: prec <= Precision
|
|
|
|
func NewDecFromBigInt(i *big.Int) Dec {
|
|
|
|
return NewDecFromBigIntWithPrec(i, 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
// create a new Dec from big integer assuming whole numbers
|
|
|
|
// CONTRACT: prec <= Precision
|
|
|
|
func NewDecFromBigIntWithPrec(i *big.Int, prec int64) Dec {
|
|
|
|
return Dec{
|
|
|
|
new(big.Int).Mul(i, precisionMultiplier(prec)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// create a new Dec from big integer assuming whole numbers
|
|
|
|
// CONTRACT: prec <= Precision
|
|
|
|
func NewDecFromInt(i Int) Dec {
|
|
|
|
return NewDecFromIntWithPrec(i, 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
// create a new Dec from big integer with decimal place at prec
|
|
|
|
// CONTRACT: prec <= Precision
|
|
|
|
func NewDecFromIntWithPrec(i Int, prec int64) Dec {
|
|
|
|
return Dec{
|
|
|
|
new(big.Int).Mul(i.BigInt(), precisionMultiplier(prec)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// create a decimal from an input decimal string.
|
|
|
|
// valid must come in the form:
|
|
|
|
// (-) whole integers (.) decimal integers
|
|
|
|
// examples of acceptable input include:
|
|
|
|
// -123.456
|
|
|
|
// 456.7890
|
|
|
|
// 345
|
|
|
|
// -456789
|
|
|
|
//
|
|
|
|
// NOTE - An error will return if more decimal places
|
|
|
|
// are provided in the string than the constant Precision.
|
|
|
|
//
|
|
|
|
// CONTRACT - This function does not mutate the input str.
|
2019-12-27 09:57:54 -08:00
|
|
|
func NewDecFromStr(str string) (Dec, error) {
|
2018-08-14 17:15:02 -07:00
|
|
|
if len(str) == 0 {
|
2019-12-27 09:57:54 -08:00
|
|
|
return Dec{}, ErrEmptyDecimalStr
|
2018-08-14 17:15:02 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// first extract any negative symbol
|
|
|
|
neg := false
|
|
|
|
if str[0] == '-' {
|
|
|
|
neg = true
|
|
|
|
str = str[1:]
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(str) == 0 {
|
2019-12-27 09:57:54 -08:00
|
|
|
return Dec{}, ErrEmptyDecimalStr
|
2018-08-14 17:15:02 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
strs := strings.Split(str, ".")
|
|
|
|
lenDecs := 0
|
|
|
|
combinedStr := strs[0]
|
2019-01-17 09:15:10 -08:00
|
|
|
|
|
|
|
if len(strs) == 2 { // has a decimal place
|
2018-08-14 17:15:02 -07:00
|
|
|
lenDecs = len(strs[1])
|
|
|
|
if lenDecs == 0 || len(combinedStr) == 0 {
|
2019-12-27 09:57:54 -08:00
|
|
|
return Dec{}, ErrInvalidDecimalLength
|
2018-08-14 17:15:02 -07:00
|
|
|
}
|
2019-08-19 09:06:27 -07:00
|
|
|
combinedStr += strs[1]
|
2018-08-14 17:15:02 -07:00
|
|
|
} else if len(strs) > 2 {
|
2019-12-27 09:57:54 -08:00
|
|
|
return Dec{}, ErrInvalidDecimalStr
|
2018-08-14 17:15:02 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if lenDecs > Precision {
|
2019-12-27 09:57:54 -08:00
|
|
|
return Dec{}, fmt.Errorf("invalid precision; max: %d, got: %d", Precision, lenDecs)
|
2018-08-14 17:15:02 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// add some extra zero's to correct to the Precision factor
|
|
|
|
zerosToAdd := Precision - lenDecs
|
|
|
|
zeros := fmt.Sprintf(`%0`+strconv.Itoa(zerosToAdd)+`s`, "")
|
2019-08-19 09:06:27 -07:00
|
|
|
combinedStr += zeros
|
2018-08-14 17:15:02 -07:00
|
|
|
|
2019-01-17 09:15:10 -08:00
|
|
|
combined, ok := new(big.Int).SetString(combinedStr, 10) // base 10
|
2018-08-14 17:15:02 -07:00
|
|
|
if !ok {
|
2019-12-27 09:57:54 -08:00
|
|
|
return Dec{}, fmt.Errorf("failed to set decimal string: %s", combinedStr)
|
2018-08-14 17:15:02 -07:00
|
|
|
}
|
2022-03-08 13:56:44 -08:00
|
|
|
if combined.BitLen() > maxDecBitLen {
|
|
|
|
return Dec{}, fmt.Errorf("decimal out of range; bitLen: got %d, max %d", combined.BitLen(), maxDecBitLen)
|
2021-04-22 03:21:27 -07:00
|
|
|
}
|
2018-08-14 17:15:02 -07:00
|
|
|
if neg {
|
|
|
|
combined = new(big.Int).Neg(combined)
|
|
|
|
}
|
2019-12-27 09:57:54 -08:00
|
|
|
|
2018-08-14 17:15:02 -07:00
|
|
|
return Dec{combined}, nil
|
|
|
|
}
|
|
|
|
|
2018-11-26 04:13:47 -08:00
|
|
|
// Decimal from string, panic on error
|
|
|
|
func MustNewDecFromStr(s string) Dec {
|
|
|
|
dec, err := NewDecFromStr(s)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
return dec
|
|
|
|
}
|
|
|
|
|
2020-01-24 07:32:00 -08:00
|
|
|
func (d Dec) IsNil() bool { return d.i == nil } // is decimal nil
|
|
|
|
func (d Dec) IsZero() bool { return (d.i).Sign() == 0 } // is equal to zero
|
|
|
|
func (d Dec) IsNegative() bool { return (d.i).Sign() == -1 } // is negative
|
|
|
|
func (d Dec) IsPositive() bool { return (d.i).Sign() == 1 } // is positive
|
|
|
|
func (d Dec) Equal(d2 Dec) bool { return (d.i).Cmp(d2.i) == 0 } // equal decimals
|
|
|
|
func (d Dec) GT(d2 Dec) bool { return (d.i).Cmp(d2.i) > 0 } // greater than
|
|
|
|
func (d Dec) GTE(d2 Dec) bool { return (d.i).Cmp(d2.i) >= 0 } // greater than or equal
|
|
|
|
func (d Dec) LT(d2 Dec) bool { return (d.i).Cmp(d2.i) < 0 } // less than
|
|
|
|
func (d Dec) LTE(d2 Dec) bool { return (d.i).Cmp(d2.i) <= 0 } // less than or equal
|
|
|
|
func (d Dec) Neg() Dec { return Dec{new(big.Int).Neg(d.i)} } // reverse the decimal sign
|
2022-02-11 01:55:41 -08:00
|
|
|
func (d Dec) NegMut() Dec { d.i.Neg(d.i); return d } // reverse the decimal sign, mutable
|
2020-01-24 07:32:00 -08:00
|
|
|
func (d Dec) Abs() Dec { return Dec{new(big.Int).Abs(d.i)} } // absolute value
|
2022-02-11 01:55:41 -08:00
|
|
|
func (d Dec) Set(d2 Dec) Dec { d.i.Set(d2.i); return d } // set to existing dec value
|
|
|
|
func (d Dec) Clone() Dec { return Dec{new(big.Int).Set(d.i)} } // clone new dec
|
2020-01-24 07:32:00 -08:00
|
|
|
|
|
|
|
// BigInt returns a copy of the underlying big.Int.
|
|
|
|
func (d Dec) BigInt() *big.Int {
|
2020-08-18 06:16:23 -07:00
|
|
|
if d.IsNil() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-02-23 00:46:01 -08:00
|
|
|
cp := new(big.Int)
|
|
|
|
return cp.Set(d.i)
|
2020-01-24 07:32:00 -08:00
|
|
|
}
|
2018-08-14 17:15:02 -07:00
|
|
|
|
2022-02-11 01:55:41 -08:00
|
|
|
func (d Dec) ImmutOp(op func(Dec, Dec) Dec, d2 Dec) Dec {
|
|
|
|
return op(d.Clone(), d2)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d Dec) ImmutOpInt(op func(Dec, Int) Dec, d2 Int) Dec {
|
|
|
|
return op(d.Clone(), d2)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d Dec) ImmutOpInt64(op func(Dec, int64) Dec, d2 int64) Dec {
|
|
|
|
// TODO: use already allocated operand bigint to avoid
|
|
|
|
// newint each time, add mutex for race condition
|
|
|
|
// Issue: https://github.com/cosmos/cosmos-sdk/issues/11166
|
|
|
|
return op(d.Clone(), d2)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d Dec) SetInt64(i int64) Dec {
|
|
|
|
d.i.SetInt64(i)
|
|
|
|
d.i.Mul(d.i, precisionReuse)
|
|
|
|
return d
|
|
|
|
}
|
|
|
|
|
2018-08-14 17:15:02 -07:00
|
|
|
// addition
|
|
|
|
func (d Dec) Add(d2 Dec) Dec {
|
2022-02-11 01:55:41 -08:00
|
|
|
return d.ImmutOp(Dec.AddMut, d2)
|
|
|
|
}
|
2018-08-14 17:15:02 -07:00
|
|
|
|
2022-02-11 01:55:41 -08:00
|
|
|
// mutable addition
|
|
|
|
func (d Dec) AddMut(d2 Dec) Dec {
|
|
|
|
d.i.Add(d.i, d2.i)
|
|
|
|
|
|
|
|
if d.i.BitLen() > maxDecBitLen {
|
2018-08-14 17:15:02 -07:00
|
|
|
panic("Int overflow")
|
|
|
|
}
|
2022-02-11 01:55:41 -08:00
|
|
|
return d
|
2018-08-14 17:15:02 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// subtraction
|
|
|
|
func (d Dec) Sub(d2 Dec) Dec {
|
2022-02-11 01:55:41 -08:00
|
|
|
return d.ImmutOp(Dec.SubMut, d2)
|
|
|
|
}
|
|
|
|
|
|
|
|
// mutable subtraction
|
|
|
|
func (d Dec) SubMut(d2 Dec) Dec {
|
|
|
|
d.i.Sub(d.i, d2.i)
|
2018-08-14 17:15:02 -07:00
|
|
|
|
2022-02-11 01:55:41 -08:00
|
|
|
if d.i.BitLen() > maxDecBitLen {
|
2018-08-14 17:15:02 -07:00
|
|
|
panic("Int overflow")
|
|
|
|
}
|
2022-02-11 01:55:41 -08:00
|
|
|
return d
|
2018-08-14 17:15:02 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// multiplication
|
|
|
|
func (d Dec) Mul(d2 Dec) Dec {
|
2022-02-11 01:55:41 -08:00
|
|
|
return d.ImmutOp(Dec.MulMut, d2)
|
|
|
|
}
|
|
|
|
|
|
|
|
// mutable multiplication
|
|
|
|
func (d Dec) MulMut(d2 Dec) Dec {
|
|
|
|
d.i.Mul(d.i, d2.i)
|
|
|
|
chopped := chopPrecisionAndRound(d.i)
|
2018-08-14 17:15:02 -07:00
|
|
|
|
2021-06-16 07:18:02 -07:00
|
|
|
if chopped.BitLen() > maxDecBitLen {
|
2018-08-14 17:15:02 -07:00
|
|
|
panic("Int overflow")
|
|
|
|
}
|
2022-02-11 01:55:41 -08:00
|
|
|
*d.i = *chopped
|
|
|
|
return d
|
2018-08-14 17:15:02 -07:00
|
|
|
}
|
|
|
|
|
2019-01-24 13:01:32 -08:00
|
|
|
// multiplication truncate
|
|
|
|
func (d Dec) MulTruncate(d2 Dec) Dec {
|
2022-02-11 01:55:41 -08:00
|
|
|
return d.ImmutOp(Dec.MulTruncateMut, d2)
|
|
|
|
}
|
2019-01-24 13:01:32 -08:00
|
|
|
|
2022-02-11 01:55:41 -08:00
|
|
|
// mutable multiplication truncage
|
|
|
|
func (d Dec) MulTruncateMut(d2 Dec) Dec {
|
|
|
|
d.i.Mul(d.i, d2.i)
|
|
|
|
chopPrecisionAndTruncate(d.i)
|
|
|
|
|
|
|
|
if d.i.BitLen() > maxDecBitLen {
|
2019-01-24 13:01:32 -08:00
|
|
|
panic("Int overflow")
|
|
|
|
}
|
2022-02-11 01:55:41 -08:00
|
|
|
return d
|
2019-01-24 13:01:32 -08:00
|
|
|
}
|
|
|
|
|
2018-09-26 03:13:40 -07:00
|
|
|
// multiplication
|
|
|
|
func (d Dec) MulInt(i Int) Dec {
|
2022-02-11 01:55:41 -08:00
|
|
|
return d.ImmutOpInt(Dec.MulIntMut, i)
|
|
|
|
}
|
2018-09-26 03:13:40 -07:00
|
|
|
|
2022-02-11 01:55:41 -08:00
|
|
|
func (d Dec) MulIntMut(i Int) Dec {
|
|
|
|
d.i.Mul(d.i, i.i)
|
|
|
|
if d.i.BitLen() > maxDecBitLen {
|
2018-09-26 03:13:40 -07:00
|
|
|
panic("Int overflow")
|
|
|
|
}
|
2022-02-11 01:55:41 -08:00
|
|
|
return d
|
2018-09-26 03:13:40 -07:00
|
|
|
}
|
|
|
|
|
2019-02-05 21:30:48 -08:00
|
|
|
// MulInt64 - multiplication with int64
|
|
|
|
func (d Dec) MulInt64(i int64) Dec {
|
2022-02-11 01:55:41 -08:00
|
|
|
return d.ImmutOpInt64(Dec.MulInt64Mut, i)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d Dec) MulInt64Mut(i int64) Dec {
|
|
|
|
d.i.Mul(d.i, big.NewInt(i))
|
2019-02-05 21:30:48 -08:00
|
|
|
|
2022-02-11 01:55:41 -08:00
|
|
|
if d.i.BitLen() > maxDecBitLen {
|
2019-02-05 21:30:48 -08:00
|
|
|
panic("Int overflow")
|
|
|
|
}
|
2022-02-11 01:55:41 -08:00
|
|
|
return d
|
2019-02-05 21:30:48 -08:00
|
|
|
}
|
|
|
|
|
2018-08-14 17:15:02 -07:00
|
|
|
// quotient
|
|
|
|
func (d Dec) Quo(d2 Dec) Dec {
|
2022-02-11 01:55:41 -08:00
|
|
|
return d.ImmutOp(Dec.QuoMut, d2)
|
|
|
|
}
|
2018-08-14 17:15:02 -07:00
|
|
|
|
2022-02-11 01:55:41 -08:00
|
|
|
// mutable quotient
|
|
|
|
func (d Dec) QuoMut(d2 Dec) Dec {
|
|
|
|
// multiply precision twice
|
|
|
|
d.i.Mul(d.i, precisionReuse)
|
|
|
|
d.i.Mul(d.i, precisionReuse)
|
|
|
|
d.i.Quo(d.i, d2.i)
|
2018-08-14 17:15:02 -07:00
|
|
|
|
2022-02-11 01:55:41 -08:00
|
|
|
chopPrecisionAndRound(d.i)
|
|
|
|
if d.i.BitLen() > maxDecBitLen {
|
2018-08-14 17:15:02 -07:00
|
|
|
panic("Int overflow")
|
|
|
|
}
|
2022-02-11 01:55:41 -08:00
|
|
|
return d
|
2018-08-14 17:15:02 -07:00
|
|
|
}
|
|
|
|
|
2019-01-24 13:01:32 -08:00
|
|
|
// quotient truncate
|
|
|
|
func (d Dec) QuoTruncate(d2 Dec) Dec {
|
2022-02-11 01:55:41 -08:00
|
|
|
return d.ImmutOp(Dec.QuoTruncateMut, d2)
|
|
|
|
}
|
2019-01-24 13:01:32 -08:00
|
|
|
|
2022-02-11 01:55:41 -08:00
|
|
|
// mutable quotient truncate
|
|
|
|
func (d Dec) QuoTruncateMut(d2 Dec) Dec {
|
|
|
|
// multiply precision twice
|
|
|
|
d.i.Mul(d.i, precisionReuse)
|
|
|
|
d.i.Mul(d.i, precisionReuse)
|
|
|
|
d.i.Quo(d.i, d2.i)
|
2019-01-24 13:01:32 -08:00
|
|
|
|
2022-02-11 01:55:41 -08:00
|
|
|
chopPrecisionAndTruncate(d.i)
|
|
|
|
if d.i.BitLen() > maxDecBitLen {
|
2019-01-24 13:01:32 -08:00
|
|
|
panic("Int overflow")
|
|
|
|
}
|
2022-02-11 01:55:41 -08:00
|
|
|
return d
|
2019-01-24 13:01:32 -08:00
|
|
|
}
|
|
|
|
|
2019-03-06 10:54:12 -08:00
|
|
|
// quotient, round up
|
|
|
|
func (d Dec) QuoRoundUp(d2 Dec) Dec {
|
2022-02-11 01:55:41 -08:00
|
|
|
return d.ImmutOp(Dec.QuoRoundupMut, d2)
|
|
|
|
}
|
2019-03-06 10:54:12 -08:00
|
|
|
|
2022-02-11 01:55:41 -08:00
|
|
|
// mutable quotient, round up
|
|
|
|
func (d Dec) QuoRoundupMut(d2 Dec) Dec {
|
|
|
|
// multiply precision twice
|
|
|
|
d.i.Mul(d.i, precisionReuse)
|
|
|
|
d.i.Mul(d.i, precisionReuse)
|
|
|
|
d.i.Quo(d.i, d2.i)
|
2019-03-06 10:54:12 -08:00
|
|
|
|
2022-02-11 01:55:41 -08:00
|
|
|
chopPrecisionAndRoundUp(d.i)
|
|
|
|
if d.i.BitLen() > maxDecBitLen {
|
2019-03-06 10:54:12 -08:00
|
|
|
panic("Int overflow")
|
|
|
|
}
|
2022-02-11 01:55:41 -08:00
|
|
|
return d
|
2019-03-06 10:54:12 -08:00
|
|
|
}
|
|
|
|
|
2018-10-12 04:15:13 -07:00
|
|
|
// quotient
|
|
|
|
func (d Dec) QuoInt(i Int) Dec {
|
2022-02-11 01:55:41 -08:00
|
|
|
return d.ImmutOpInt(Dec.QuoIntMut, i)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d Dec) QuoIntMut(i Int) Dec {
|
|
|
|
d.i.Quo(d.i, i.i)
|
|
|
|
return d
|
2018-10-12 04:15:13 -07:00
|
|
|
}
|
|
|
|
|
2019-02-05 21:30:48 -08:00
|
|
|
// QuoInt64 - quotient with int64
|
|
|
|
func (d Dec) QuoInt64(i int64) Dec {
|
2022-02-11 01:55:41 -08:00
|
|
|
return d.ImmutOpInt64(Dec.QuoInt64Mut, i)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d Dec) QuoInt64Mut(i int64) Dec {
|
|
|
|
d.i.Quo(d.i, big.NewInt(i))
|
|
|
|
return d
|
2019-02-05 21:30:48 -08:00
|
|
|
}
|
|
|
|
|
2020-01-04 12:16:12 -08:00
|
|
|
// ApproxRoot returns an approximate estimation of a Dec's positive real nth root
|
|
|
|
// using Newton's method (where n is positive). The algorithm starts with some guess and
|
2019-10-18 09:25:07 -07:00
|
|
|
// computes the sequence of improved guesses until an answer converges to an
|
2020-01-04 12:16:12 -08:00
|
|
|
// approximate answer. It returns `|d|.ApproxRoot() * -1` if input is negative.
|
2020-08-29 03:13:36 -07:00
|
|
|
// A maximum number of 100 iterations is used a backup boundary condition for
|
|
|
|
// cases where the answer never converges enough to satisfy the main condition.
|
2020-01-04 12:16:12 -08:00
|
|
|
func (d Dec) ApproxRoot(root uint64) (guess Dec, err error) {
|
|
|
|
defer func() {
|
|
|
|
if r := recover(); r != nil {
|
|
|
|
var ok bool
|
|
|
|
err, ok = r.(error)
|
|
|
|
if !ok {
|
|
|
|
err = errors.New("out of bounds")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2019-10-18 09:25:07 -07:00
|
|
|
if d.IsNegative() {
|
2022-02-11 01:55:41 -08:00
|
|
|
absRoot, err := d.Neg().ApproxRoot(root)
|
|
|
|
return absRoot.NegMut(), err
|
2020-01-04 12:16:12 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
if root == 1 || d.IsZero() || d.Equal(OneDec()) {
|
|
|
|
return d, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if root == 0 {
|
|
|
|
return OneDec(), nil
|
2019-10-18 09:25:07 -07:00
|
|
|
}
|
|
|
|
|
2020-01-04 12:16:12 -08:00
|
|
|
guess, delta := OneDec(), OneDec()
|
|
|
|
|
2020-08-29 03:13:36 -07:00
|
|
|
for iter := 0; delta.Abs().GT(SmallestDec()) && iter < maxApproxRootIterations; iter++ {
|
2020-01-04 12:16:12 -08:00
|
|
|
prev := guess.Power(root - 1)
|
|
|
|
if prev.IsZero() {
|
|
|
|
prev = SmallestDec()
|
|
|
|
}
|
2022-02-11 01:55:41 -08:00
|
|
|
delta.Set(d).QuoMut(prev)
|
|
|
|
delta.SubMut(guess)
|
|
|
|
delta.QuoInt64Mut(int64(root))
|
2020-01-04 12:16:12 -08:00
|
|
|
|
2022-02-11 01:55:41 -08:00
|
|
|
guess.AddMut(delta)
|
2019-10-18 09:25:07 -07:00
|
|
|
}
|
|
|
|
|
2020-01-04 12:16:12 -08:00
|
|
|
return guess, nil
|
|
|
|
}
|
2019-10-18 09:25:07 -07:00
|
|
|
|
2020-01-04 12:16:12 -08:00
|
|
|
// Power returns a the result of raising to a positive integer power
|
|
|
|
func (d Dec) Power(power uint64) Dec {
|
2022-02-11 01:55:41 -08:00
|
|
|
res := Dec{new(big.Int).Set(d.i)}
|
|
|
|
return res.PowerMut(power)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d Dec) PowerMut(power uint64) Dec {
|
2020-01-04 12:16:12 -08:00
|
|
|
if power == 0 {
|
2022-02-11 01:55:41 -08:00
|
|
|
d.SetInt64(1)
|
|
|
|
return d
|
2019-10-18 09:25:07 -07:00
|
|
|
}
|
2020-01-04 12:16:12 -08:00
|
|
|
tmp := OneDec()
|
2020-05-02 12:26:59 -07:00
|
|
|
|
2020-01-04 12:16:12 -08:00
|
|
|
for i := power; i > 1; {
|
2020-12-20 10:41:59 -08:00
|
|
|
if i%2 != 0 {
|
2022-02-11 01:55:41 -08:00
|
|
|
tmp.MulMut(d)
|
2020-01-04 12:16:12 -08:00
|
|
|
}
|
2020-12-20 10:41:59 -08:00
|
|
|
i /= 2
|
2022-02-11 01:55:41 -08:00
|
|
|
d.MulMut(d)
|
2020-01-04 12:16:12 -08:00
|
|
|
}
|
2020-05-02 12:26:59 -07:00
|
|
|
|
2022-02-11 01:55:41 -08:00
|
|
|
return d.MulMut(tmp)
|
2020-01-04 12:16:12 -08:00
|
|
|
}
|
2019-10-18 09:25:07 -07:00
|
|
|
|
2020-01-04 12:16:12 -08:00
|
|
|
// ApproxSqrt is a wrapper around ApproxRoot for the common special case
|
|
|
|
// of finding the square root of a number. It returns -(sqrt(abs(d)) if input is negative.
|
|
|
|
func (d Dec) ApproxSqrt() (Dec, error) {
|
|
|
|
return d.ApproxRoot(2)
|
2019-10-18 09:25:07 -07:00
|
|
|
}
|
|
|
|
|
2018-10-23 11:33:39 -07:00
|
|
|
// is integer, e.g. decimals are zero
|
2018-10-22 01:18:10 -07:00
|
|
|
func (d Dec) IsInteger() bool {
|
2020-01-24 07:32:00 -08:00
|
|
|
return new(big.Int).Rem(d.i, precisionReuse).Sign() == 0
|
2018-10-22 01:18:10 -07:00
|
|
|
}
|
|
|
|
|
2018-11-26 04:13:47 -08:00
|
|
|
// format decimal state
|
|
|
|
func (d Dec) Format(s fmt.State, verb rune) {
|
|
|
|
_, err := s.Write([]byte(d.String()))
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-14 17:15:02 -07:00
|
|
|
func (d Dec) String() string {
|
2020-01-24 07:32:00 -08:00
|
|
|
if d.i == nil {
|
|
|
|
return d.i.String()
|
2019-03-16 16:14:37 -07:00
|
|
|
}
|
|
|
|
|
2018-12-07 16:04:52 -08:00
|
|
|
isNeg := d.IsNegative()
|
2020-05-02 12:26:59 -07:00
|
|
|
|
|
|
|
if isNeg {
|
2018-12-07 16:04:52 -08:00
|
|
|
d = d.Neg()
|
|
|
|
}
|
2019-01-17 09:15:10 -08:00
|
|
|
|
2020-01-24 07:32:00 -08:00
|
|
|
bzInt, err := d.i.MarshalText()
|
2018-10-20 13:32:43 -07:00
|
|
|
if err != nil {
|
|
|
|
return ""
|
2018-08-14 17:15:02 -07:00
|
|
|
}
|
2019-01-17 09:15:10 -08:00
|
|
|
inputSize := len(bzInt)
|
|
|
|
|
|
|
|
var bzStr []byte
|
|
|
|
|
2018-10-20 13:32:43 -07:00
|
|
|
// TODO: Remove trailing zeros
|
|
|
|
// case 1, purely decimal
|
2019-01-17 09:15:10 -08:00
|
|
|
if inputSize <= Precision {
|
|
|
|
bzStr = make([]byte, Precision+2)
|
|
|
|
|
2018-10-20 13:32:43 -07:00
|
|
|
// 0. prefix
|
2019-01-17 09:15:10 -08:00
|
|
|
bzStr[0] = byte('0')
|
|
|
|
bzStr[1] = byte('.')
|
|
|
|
|
2018-10-20 13:32:43 -07:00
|
|
|
// set relevant digits to 0
|
2019-01-17 09:15:10 -08:00
|
|
|
for i := 0; i < Precision-inputSize; i++ {
|
|
|
|
bzStr[i+2] = byte('0')
|
2018-10-20 13:32:43 -07:00
|
|
|
}
|
2019-01-17 09:15:10 -08:00
|
|
|
|
|
|
|
// set final digits
|
|
|
|
copy(bzStr[2+(Precision-inputSize):], bzInt)
|
2018-10-20 13:32:43 -07:00
|
|
|
} else {
|
2018-10-20 18:02:17 -07:00
|
|
|
// inputSize + 1 to account for the decimal point that is being added
|
2019-01-17 09:15:10 -08:00
|
|
|
bzStr = make([]byte, inputSize+1)
|
|
|
|
decPointPlace := inputSize - Precision
|
|
|
|
|
|
|
|
copy(bzStr, bzInt[:decPointPlace]) // pre-decimal digits
|
|
|
|
bzStr[decPointPlace] = byte('.') // decimal point
|
|
|
|
copy(bzStr[decPointPlace+1:], bzInt[decPointPlace:]) // post-decimal digits
|
2018-08-14 17:15:02 -07:00
|
|
|
}
|
2019-01-17 09:15:10 -08:00
|
|
|
|
2018-12-07 16:04:52 -08:00
|
|
|
if isNeg {
|
2019-01-17 09:15:10 -08:00
|
|
|
return "-" + string(bzStr)
|
2018-12-07 16:04:52 -08:00
|
|
|
}
|
2019-03-16 16:14:37 -07:00
|
|
|
|
2019-01-17 09:15:10 -08:00
|
|
|
return string(bzStr)
|
2018-08-14 17:15:02 -07:00
|
|
|
}
|
|
|
|
|
2021-05-26 02:02:14 -07:00
|
|
|
// Float64 returns the float64 representation of a Dec.
|
|
|
|
// Will return the error if the conversion failed.
|
|
|
|
func (d Dec) Float64() (float64, error) {
|
|
|
|
return strconv.ParseFloat(d.String(), 64)
|
|
|
|
}
|
|
|
|
|
|
|
|
// MustFloat64 returns the float64 representation of a Dec.
|
|
|
|
// Would panic if the conversion failed.
|
|
|
|
func (d Dec) MustFloat64() float64 {
|
|
|
|
if value, err := strconv.ParseFloat(d.String(), 64); err != nil {
|
|
|
|
panic(err)
|
|
|
|
} else {
|
|
|
|
return value
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-14 17:15:02 -07:00
|
|
|
// ____
|
|
|
|
// __| |__ "chop 'em
|
|
|
|
// ` \ round!"
|
|
|
|
// ___|| ~ _ -bankers
|
|
|
|
// | | __
|
|
|
|
// | | | __|__|__
|
|
|
|
// |_____: / | $$$ |
|
|
|
|
// |________|
|
|
|
|
|
|
|
|
// Remove a Precision amount of rightmost digits and perform bankers rounding
|
|
|
|
// on the remainder (gaussian rounding) on the digits which have been removed.
|
|
|
|
//
|
2018-08-14 18:49:40 -07:00
|
|
|
// Mutates the input. Use the non-mutative version if that is undesired
|
2018-08-14 17:15:02 -07:00
|
|
|
func chopPrecisionAndRound(d *big.Int) *big.Int {
|
|
|
|
// remove the negative and add it back when returning
|
|
|
|
if d.Sign() == -1 {
|
|
|
|
// make d positive, compute chopped value, and then un-mutate d
|
|
|
|
d = d.Neg(d)
|
|
|
|
d = chopPrecisionAndRound(d)
|
|
|
|
d = d.Neg(d)
|
|
|
|
return d
|
|
|
|
}
|
|
|
|
|
2019-01-18 08:45:20 -08:00
|
|
|
// get the truncated quotient and remainder
|
2018-08-14 18:49:40 -07:00
|
|
|
quo, rem := d, big.NewInt(0)
|
2018-08-14 17:15:02 -07:00
|
|
|
quo, rem = quo.QuoRem(d, precisionReuse, rem)
|
|
|
|
|
|
|
|
if rem.Sign() == 0 { // remainder is zero
|
|
|
|
return quo
|
|
|
|
}
|
|
|
|
|
|
|
|
switch rem.Cmp(fivePrecision) {
|
|
|
|
case -1:
|
|
|
|
return quo
|
|
|
|
case 1:
|
|
|
|
return quo.Add(quo, oneInt)
|
|
|
|
default: // bankers rounding must take place
|
|
|
|
// always round to an even number
|
|
|
|
if quo.Bit(0) == 0 {
|
|
|
|
return quo
|
|
|
|
}
|
|
|
|
return quo.Add(quo, oneInt)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-06 10:54:12 -08:00
|
|
|
func chopPrecisionAndRoundUp(d *big.Int) *big.Int {
|
|
|
|
// remove the negative and add it back when returning
|
|
|
|
if d.Sign() == -1 {
|
|
|
|
// make d positive, compute chopped value, and then un-mutate d
|
|
|
|
d = d.Neg(d)
|
|
|
|
// truncate since d is negative...
|
2022-02-11 01:55:41 -08:00
|
|
|
chopPrecisionAndTruncate(d)
|
2019-03-06 10:54:12 -08:00
|
|
|
d = d.Neg(d)
|
|
|
|
return d
|
|
|
|
}
|
|
|
|
|
|
|
|
// get the truncated quotient and remainder
|
|
|
|
quo, rem := d, big.NewInt(0)
|
|
|
|
quo, rem = quo.QuoRem(d, precisionReuse, rem)
|
|
|
|
|
|
|
|
if rem.Sign() == 0 { // remainder is zero
|
|
|
|
return quo
|
|
|
|
}
|
|
|
|
|
|
|
|
return quo.Add(quo, oneInt)
|
|
|
|
}
|
|
|
|
|
2018-08-14 18:49:40 -07:00
|
|
|
func chopPrecisionAndRoundNonMutative(d *big.Int) *big.Int {
|
|
|
|
tmp := new(big.Int).Set(d)
|
|
|
|
return chopPrecisionAndRound(tmp)
|
|
|
|
}
|
|
|
|
|
2018-08-14 17:15:02 -07:00
|
|
|
// RoundInt64 rounds the decimal using bankers rounding
|
|
|
|
func (d Dec) RoundInt64() int64 {
|
2020-01-24 07:32:00 -08:00
|
|
|
chopped := chopPrecisionAndRoundNonMutative(d.i)
|
2018-08-14 17:15:02 -07:00
|
|
|
if !chopped.IsInt64() {
|
|
|
|
panic("Int64() out of bound")
|
|
|
|
}
|
|
|
|
return chopped.Int64()
|
|
|
|
}
|
|
|
|
|
|
|
|
// RoundInt round the decimal using bankers rounding
|
|
|
|
func (d Dec) RoundInt() Int {
|
2020-01-24 07:32:00 -08:00
|
|
|
return NewIntFromBigInt(chopPrecisionAndRoundNonMutative(d.i))
|
2018-08-14 17:15:02 -07:00
|
|
|
}
|
|
|
|
|
types: make chopPrecisionAndTruncate always non-mutative (#9250)
Currently, chopPrecisionAndTruncate/chopPrecisionAndTruncateNonMutative
are doing the same thing, except that the former do it mutatively.
Looking at the usages of chopPrecisionAndTruncate, the callers must
alloc a new big.Int before calling it anyway, so any mutation will be
done with this new allocated big.Int, instead of input argument.
So, by making chopPrecisionAndTruncate always non-mutative, we simplify
the code, and make the code faster, less allocation in some benchmarks.
Benchmark for github.com/cosmos/cosmos-sdk/types:
name old time/op new time/op delta
CoinsAdditionIntersect/sizes:_A_1,_B_1-8 35.4ns ± 0% 35.1ns ± 0% -0.92% (p=0.008 n=5+5)
CoinsAdditionIntersect/sizes:_A_5,_B_5-8 366ns ± 1% 363ns ± 1% ~ (p=0.056 n=5+5)
CoinsAdditionIntersect/sizes:_A_5,_B_20-8 557ns ± 0% 553ns ± 0% -0.64% (p=0.008 n=5+5)
CoinsAdditionIntersect/sizes:_A_1,_B_1000-8 6.17µs ± 0% 6.18µs ± 0% ~ (p=0.151 n=5+5)
CoinsAdditionIntersect/sizes:_A_2,_B_1000-8 6.30µs ± 0% 6.32µs ± 0% +0.23% (p=0.008 n=5+5)
CoinsAdditionNoIntersect/sizes:_A_1,_B_1-8 30.2ns ± 0% 29.9ns ± 1% -0.98% (p=0.008 n=5+5)
CoinsAdditionNoIntersect/sizes:_A_5,_B_5-8 226ns ± 0% 224ns ± 0% -0.54% (p=0.008 n=5+5)
CoinsAdditionNoIntersect/sizes:_A_5,_B_20-8 373ns ± 0% 372ns ± 0% -0.26% (p=0.040 n=5+5)
CoinsAdditionNoIntersect/sizes:_A_1,_B_1000-8 6.21µs ± 0% 6.22µs ± 0% ~ (p=0.421 n=5+5)
CoinsAdditionNoIntersect/sizes:_A_2,_B_1000-8 6.22µs ± 0% 6.23µs ± 0% ~ (p=0.286 n=5+5)
CoinsAdditionNoIntersect/sizes:_A_1000,_B_2-8 6.22µs ± 0% 6.23µs ± 0% ~ (p=0.143 n=5+5)
ParseCoin-8 5.46µs ± 0% 5.39µs ± 0% -1.26% (p=0.008 n=5+5)
UintMarshal-8 1.01µs ± 0% 1.00µs ± 0% -1.10% (p=0.008 n=5+5)
IntMarshal-8 764ns ± 0% 758ns ± 1% -0.79% (p=0.024 n=5+5)
MarshalTo-8 192ns ± 0% 190ns ± 0% -1.03% (p=0.008 n=5+5)
name old alloc/op new alloc/op delta
CoinsAdditionIntersect/sizes:_A_1,_B_1-8 32.0B ± 0% 32.0B ± 0% ~ (all equal)
CoinsAdditionIntersect/sizes:_A_5,_B_5-8 520B ± 0% 520B ± 0% ~ (all equal)
CoinsAdditionIntersect/sizes:_A_5,_B_20-8 1.38kB ± 0% 1.38kB ± 0% ~ (all equal)
CoinsAdditionIntersect/sizes:_A_1,_B_1000-8 49.2kB ± 0% 49.2kB ± 0% ~ (all equal)
CoinsAdditionIntersect/sizes:_A_2,_B_1000-8 49.3kB ± 0% 49.3kB ± 0% ~ (all equal)
CoinsAdditionNoIntersect/sizes:_A_1,_B_1-8 24.0B ± 0% 24.0B ± 0% ~ (all equal)
CoinsAdditionNoIntersect/sizes:_A_5,_B_5-8 488B ± 0% 488B ± 0% ~ (all equal)
CoinsAdditionNoIntersect/sizes:_A_5,_B_20-8 1.35kB ± 0% 1.35kB ± 0% ~ (all equal)
CoinsAdditionNoIntersect/sizes:_A_1,_B_1000-8 49.2kB ± 0% 49.2kB ± 0% ~ (all equal)
CoinsAdditionNoIntersect/sizes:_A_2,_B_1000-8 49.2kB ± 0% 49.2kB ± 0% ~ (all equal)
CoinsAdditionNoIntersect/sizes:_A_1000,_B_2-8 49.2kB ± 0% 49.2kB ± 0% ~ (all equal)
ParseCoin-8 2.21kB ± 0% 2.21kB ± 0% ~ (all equal)
UintMarshal-8 392B ± 0% 392B ± 0% ~ (all equal)
IntMarshal-8 168B ± 0% 168B ± 0% ~ (all equal)
MarshalTo-8 80.0B ± 0% 80.0B ± 0% ~ (all equal)
name old allocs/op new allocs/op delta
CoinsAdditionIntersect/sizes:_A_1,_B_1-8 1.00 ± 0% 1.00 ± 0% ~ (all equal)
CoinsAdditionIntersect/sizes:_A_5,_B_5-8 12.0 ± 0% 12.0 ± 0% ~ (all equal)
CoinsAdditionIntersect/sizes:_A_5,_B_20-8 14.0 ± 0% 14.0 ± 0% ~ (all equal)
CoinsAdditionIntersect/sizes:_A_1,_B_1000-8 3.00 ± 0% 3.00 ± 0% ~ (all equal)
CoinsAdditionIntersect/sizes:_A_2,_B_1000-8 6.00 ± 0% 6.00 ± 0% ~ (all equal)
CoinsAdditionNoIntersect/sizes:_A_1,_B_1-8 1.00 ± 0% 1.00 ± 0% ~ (all equal)
CoinsAdditionNoIntersect/sizes:_A_5,_B_5-8 5.00 ± 0% 5.00 ± 0% ~ (all equal)
CoinsAdditionNoIntersect/sizes:_A_5,_B_20-8 6.00 ± 0% 6.00 ± 0% ~ (all equal)
CoinsAdditionNoIntersect/sizes:_A_1,_B_1000-8 3.00 ± 0% 3.00 ± 0% ~ (all equal)
CoinsAdditionNoIntersect/sizes:_A_2,_B_1000-8 4.00 ± 0% 4.00 ± 0% ~ (all equal)
CoinsAdditionNoIntersect/sizes:_A_1000,_B_2-8 3.00 ± 0% 3.00 ± 0% ~ (all equal)
ParseCoin-8 71.0 ± 0% 71.0 ± 0% ~ (all equal)
UintMarshal-8 25.0 ± 0% 25.0 ± 0% ~ (all equal)
IntMarshal-8 18.0 ± 0% 18.0 ± 0% ~ (all equal)
MarshalTo-8 2.00 ± 0% 2.00 ± 0% ~ (all equal)
name old speed new speed delta
UintMarshal-8 6.91MB/s ± 0% 6.99MB/s ± 0% +1.04% (p=0.008 n=5+5)
IntMarshal-8 9.16MB/s ± 0% 9.23MB/s ± 1% +0.83% (p=0.032 n=5+5)
Benchmark for github.com/cosmos/cosmos-sdk/x/mint/types:
name old time/op new time/op delta
BlockProvision-8 263ns ± 0% 257ns ± 0% -2.23% (p=0.008 n=5+5)
name old alloc/op new alloc/op delta
BlockProvision-8 112B ± 0% 104B ± 0% -7.14% (p=0.008 n=5+5)
name old allocs/op new allocs/op delta
BlockProvision-8 6.00 ± 0% 5.00 ± 0% -16.67% (p=0.008 n=5+5)
Fixes #9249
Co-authored-by: Emmanuel T Odeke <emmanuel@orijtech.com>
2021-05-03 16:32:15 -07:00
|
|
|
// chopPrecisionAndTruncate is similar to chopPrecisionAndRound,
|
|
|
|
// but always rounds down. It does not mutate the input.
|
2022-02-11 01:55:41 -08:00
|
|
|
func chopPrecisionAndTruncate(d *big.Int) {
|
|
|
|
d.Quo(d, precisionReuse)
|
|
|
|
}
|
|
|
|
|
|
|
|
func chopPrecisionAndTruncateNonMutative(d *big.Int) *big.Int {
|
|
|
|
tmp := new(big.Int).Set(d)
|
|
|
|
chopPrecisionAndTruncate(tmp)
|
|
|
|
return tmp
|
2018-09-24 21:18:18 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// TruncateInt64 truncates the decimals from the number and returns an int64
|
|
|
|
func (d Dec) TruncateInt64() int64 {
|
2022-02-11 01:55:41 -08:00
|
|
|
chopped := chopPrecisionAndTruncateNonMutative(d.i)
|
2018-09-24 21:18:18 -07:00
|
|
|
if !chopped.IsInt64() {
|
|
|
|
panic("Int64() out of bound")
|
|
|
|
}
|
|
|
|
return chopped.Int64()
|
|
|
|
}
|
|
|
|
|
|
|
|
// TruncateInt truncates the decimals from the number and returns an Int
|
|
|
|
func (d Dec) TruncateInt() Int {
|
2022-02-11 01:55:41 -08:00
|
|
|
return NewIntFromBigInt(chopPrecisionAndTruncateNonMutative(d.i))
|
2018-09-24 21:18:18 -07:00
|
|
|
}
|
|
|
|
|
2018-12-08 07:18:04 -08:00
|
|
|
// TruncateDec truncates the decimals from the number and returns a Dec
|
|
|
|
func (d Dec) TruncateDec() Dec {
|
2022-02-11 01:55:41 -08:00
|
|
|
return NewDecFromBigInt(chopPrecisionAndTruncateNonMutative(d.i))
|
2018-12-08 07:18:04 -08:00
|
|
|
}
|
|
|
|
|
2019-01-18 08:45:20 -08:00
|
|
|
// Ceil returns the smallest interger value (as a decimal) that is greater than
|
|
|
|
// or equal to the given decimal.
|
|
|
|
func (d Dec) Ceil() Dec {
|
2020-01-24 07:32:00 -08:00
|
|
|
tmp := new(big.Int).Set(d.i)
|
2019-01-18 08:45:20 -08:00
|
|
|
|
|
|
|
quo, rem := tmp, big.NewInt(0)
|
|
|
|
quo, rem = quo.QuoRem(tmp, precisionReuse, rem)
|
|
|
|
|
|
|
|
// no need to round with a zero remainder regardless of sign
|
|
|
|
if rem.Cmp(zeroInt) == 0 {
|
|
|
|
return NewDecFromBigInt(quo)
|
|
|
|
}
|
|
|
|
|
|
|
|
if rem.Sign() == -1 {
|
|
|
|
return NewDecFromBigInt(quo)
|
|
|
|
}
|
|
|
|
|
|
|
|
return NewDecFromBigInt(quo.Add(quo, oneInt))
|
|
|
|
}
|
|
|
|
|
2019-12-05 09:55:43 -08:00
|
|
|
// MaxSortableDec is the largest Dec that can be passed into SortableDecBytes()
|
|
|
|
// Its negative form is the least Dec that can be passed in.
|
2022-02-11 01:55:41 -08:00
|
|
|
var MaxSortableDec Dec
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
MaxSortableDec = OneDec().Quo(SmallestDec())
|
|
|
|
}
|
2019-12-05 09:55:43 -08:00
|
|
|
|
|
|
|
// ValidSortableDec ensures that a Dec is within the sortable bounds,
|
|
|
|
// a Dec can't have a precision of less than 10^-18.
|
|
|
|
// Max sortable decimal was set to the reciprocal of SmallestDec.
|
|
|
|
func ValidSortableDec(dec Dec) bool {
|
|
|
|
return dec.Abs().LTE(MaxSortableDec)
|
|
|
|
}
|
|
|
|
|
|
|
|
// SortableDecBytes returns a byte slice representation of a Dec that can be sorted.
|
|
|
|
// Left and right pads with 0s so there are 18 digits to left and right of the decimal point.
|
|
|
|
// For this reason, there is a maximum and minimum value for this, enforced by ValidSortableDec.
|
|
|
|
func SortableDecBytes(dec Dec) []byte {
|
|
|
|
if !ValidSortableDec(dec) {
|
|
|
|
panic("dec must be within bounds")
|
|
|
|
}
|
|
|
|
// Instead of adding an extra byte to all sortable decs in order to handle max sortable, we just
|
|
|
|
// makes its bytes be "max" which comes after all numbers in ASCIIbetical order
|
|
|
|
if dec.Equal(MaxSortableDec) {
|
|
|
|
return []byte("max")
|
|
|
|
}
|
|
|
|
// For the same reason, we make the bytes of minimum sortable dec be --, which comes before all numbers.
|
|
|
|
if dec.Equal(MaxSortableDec.Neg()) {
|
|
|
|
return []byte("--")
|
|
|
|
}
|
|
|
|
// We move the negative sign to the front of all the left padded 0s, to make negative numbers come before positive numbers
|
|
|
|
if dec.IsNegative() {
|
|
|
|
return append([]byte("-"), []byte(fmt.Sprintf(fmt.Sprintf("%%0%ds", Precision*2+1), dec.Abs().String()))...)
|
|
|
|
}
|
|
|
|
return []byte(fmt.Sprintf(fmt.Sprintf("%%0%ds", Precision*2+1), dec.String()))
|
|
|
|
}
|
|
|
|
|
2018-08-14 17:15:02 -07:00
|
|
|
// reuse nil values
|
2020-01-24 07:32:00 -08:00
|
|
|
var nilJSON []byte
|
2018-08-14 17:15:02 -07:00
|
|
|
|
|
|
|
func init() {
|
|
|
|
empty := new(big.Int)
|
2020-01-24 07:32:00 -08:00
|
|
|
bz, _ := empty.MarshalText()
|
|
|
|
nilJSON, _ = json.Marshal(string(bz))
|
2018-08-14 17:15:02 -07:00
|
|
|
}
|
|
|
|
|
2018-10-16 09:29:32 -07:00
|
|
|
// MarshalJSON marshals the decimal
|
2018-08-14 17:15:02 -07:00
|
|
|
func (d Dec) MarshalJSON() ([]byte, error) {
|
2020-01-24 07:32:00 -08:00
|
|
|
if d.i == nil {
|
2018-08-14 17:15:02 -07:00
|
|
|
return nilJSON, nil
|
|
|
|
}
|
2018-10-20 13:32:43 -07:00
|
|
|
return json.Marshal(d.String())
|
2018-08-14 17:15:02 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// UnmarshalJSON defines custom decoding scheme
|
|
|
|
func (d *Dec) UnmarshalJSON(bz []byte) error {
|
2020-01-24 07:32:00 -08:00
|
|
|
if d.i == nil {
|
|
|
|
d.i = new(big.Int)
|
2018-08-14 17:15:02 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
var text string
|
|
|
|
err := json.Unmarshal(bz, &text)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-01-24 07:32:00 -08:00
|
|
|
|
2018-10-16 09:29:32 -07:00
|
|
|
// TODO: Reuse dec allocation
|
|
|
|
newDec, err := NewDecFromStr(text)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-01-24 07:32:00 -08:00
|
|
|
|
|
|
|
d.i = newDec.i
|
2018-10-16 09:29:32 -07:00
|
|
|
return nil
|
2018-08-14 17:15:02 -07:00
|
|
|
}
|
|
|
|
|
2020-01-24 07:32:00 -08:00
|
|
|
// MarshalYAML returns the YAML representation.
|
|
|
|
func (d Dec) MarshalYAML() (interface{}, error) {
|
|
|
|
return d.String(), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Marshal implements the gogo proto custom type interface.
|
|
|
|
func (d Dec) Marshal() ([]byte, error) {
|
|
|
|
if d.i == nil {
|
|
|
|
d.i = new(big.Int)
|
|
|
|
}
|
|
|
|
return d.i.MarshalText()
|
|
|
|
}
|
|
|
|
|
|
|
|
// MarshalTo implements the gogo proto custom type interface.
|
|
|
|
func (d *Dec) MarshalTo(data []byte) (n int, err error) {
|
|
|
|
if d.i == nil {
|
|
|
|
d.i = new(big.Int)
|
|
|
|
}
|
2020-07-12 06:26:51 -07:00
|
|
|
|
|
|
|
if d.i.Cmp(zeroInt) == 0 {
|
2020-01-24 07:32:00 -08:00
|
|
|
copy(data, []byte{0x30})
|
|
|
|
return 1, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
bz, err := d.Marshal()
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
|
|
|
|
copy(data, bz)
|
|
|
|
return len(bz), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Unmarshal implements the gogo proto custom type interface.
|
|
|
|
func (d *Dec) Unmarshal(data []byte) error {
|
|
|
|
if len(data) == 0 {
|
|
|
|
d = nil
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if d.i == nil {
|
|
|
|
d.i = new(big.Int)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := d.i.UnmarshalText(data); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-03-08 13:56:44 -08:00
|
|
|
if d.i.BitLen() > maxDecBitLen {
|
|
|
|
return fmt.Errorf("decimal out of range; got: %d, max: %d", d.i.BitLen(), maxDecBitLen)
|
2020-01-24 07:32:00 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Size implements the gogo proto custom type interface.
|
|
|
|
func (d *Dec) Size() int {
|
|
|
|
bz, _ := d.Marshal()
|
|
|
|
return len(bz)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Override Amino binary serialization by proxying to protobuf.
|
|
|
|
func (d Dec) MarshalAmino() ([]byte, error) { return d.Marshal() }
|
|
|
|
func (d *Dec) UnmarshalAmino(bz []byte) error { return d.Unmarshal(bz) }
|
2019-06-18 09:03:16 -07:00
|
|
|
|
2020-02-06 11:21:02 -08:00
|
|
|
func (dp DecProto) String() string {
|
|
|
|
return dp.Dec.String()
|
|
|
|
}
|
|
|
|
|
2018-08-14 17:15:02 -07:00
|
|
|
// helpers
|
|
|
|
|
|
|
|
// test if two decimal arrays are equal
|
|
|
|
func DecsEqual(d1s, d2s []Dec) bool {
|
|
|
|
if len(d1s) != len(d2s) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, d1 := range d1s {
|
|
|
|
if !d1.Equal(d2s[i]) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
// minimum decimal between two
|
|
|
|
func MinDec(d1, d2 Dec) Dec {
|
|
|
|
if d1.LT(d2) {
|
|
|
|
return d1
|
|
|
|
}
|
|
|
|
return d2
|
|
|
|
}
|
|
|
|
|
2018-08-31 17:01:23 -07:00
|
|
|
// maximum decimal between two
|
|
|
|
func MaxDec(d1, d2 Dec) Dec {
|
|
|
|
if d1.LT(d2) {
|
|
|
|
return d2
|
|
|
|
}
|
|
|
|
return d1
|
|
|
|
}
|
|
|
|
|
2018-08-14 17:15:02 -07:00
|
|
|
// intended to be used with require/assert: require.True(DecEq(...))
|
2018-10-04 00:00:24 -07:00
|
|
|
func DecEq(t *testing.T, exp, got Dec) (*testing.T, bool, string, string, string) {
|
|
|
|
return t, exp.Equal(got), "expected:\t%v\ngot:\t\t%v", exp.String(), got.String()
|
2018-08-14 17:15:02 -07:00
|
|
|
}
|
2021-11-30 01:06:37 -08:00
|
|
|
|
|
|
|
func DecApproxEq(t *testing.T, d1 Dec, d2 Dec, tol Dec) (*testing.T, bool, string, string, string) {
|
|
|
|
diff := d1.Sub(d2).Abs()
|
|
|
|
return t, diff.LTE(tol), "expected |d1 - d2| <:\t%v\ngot |d1 - d2| = \t\t%v", tol.String(), diff.String()
|
|
|
|
}
|