refactor: Move sdk.Dec to math package (#12634)

## Description





---

### Author Checklist

*All items are required. Please add a note to the item if the item is not applicable and
please add links to any relevant follow up issues.*

I have...

- [ ] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] added `!` to the type prefix if API or client breaking change
- [ ] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/main/CONTRIBUTING.md#pr-targeting))
- [ ] provided a link to the relevant issue or specification
- [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/main/docs/building-modules)
- [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/main/CONTRIBUTING.md#testing)
- [ ] added a changelog entry to `CHANGELOG.md`
- [ ] included comments for [documenting Go code](https://blog.golang.org/godoc)
- [ ] updated the relevant documentation or specification
- [ ] reviewed "Files changed" and left comments if necessary
- [ ] confirmed all CI checks have passed

### Reviewers Checklist

*All items are required. Please add a note if the item is not applicable and please add
your handle next to the items reviewed if you only reviewed selected items.*

I have...

- [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] confirmed `!` in the type prefix if API or client breaking change
- [ ] confirmed all author checklist items have been addressed 
- [ ] reviewed state machine logic
- [ ] reviewed API design and naming
- [ ] reviewed documentation is accurate
- [ ] reviewed tests and test coverage
- [ ] manually tested (if applicable)
This commit is contained in:
Amaury 2022-07-20 17:13:45 +02:00 committed by GitHub
parent 7aef065c2f
commit eee23d9531
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 840 additions and 798 deletions

View File

@ -47,6 +47,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
### Improvements
* [#12634](https://github.com/cosmos/cosmos-sdk/pull/12634) Move `sdk.Dec` to math package.
* [#12596](https://github.com/cosmos/cosmos-sdk/pull/12596) Remove all imports of the non-existent gogo/protobuf v1.3.3 to ease downstream use and go workspaces.
* [#12589](https://github.com/cosmos/cosmos-sdk/pull/12589) Allow zero gas in simulation mode.
* [#12576](https://github.com/cosmos/cosmos-sdk/pull/12576) Remove dependency on cosmos/keyring and upgrade to 99designs/keyring v1.2.1

1
go.mod
View File

@ -283,6 +283,7 @@ replace (
cosmossdk.io/api => ./api
cosmossdk.io/core => ./core
cosmossdk.io/depinject => ./depinject
cosmossdk.io/math => ./math
github.com/cosmos/cosmos-sdk/db => ./db
github.com/cosmos/cosmos-sdk/store/tools/ics23 => ./store/tools/ics23

2
go.sum
View File

@ -69,8 +69,6 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f
contrib.go.opencensus.io/exporter/stackdriver v0.13.4/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc=
cosmossdk.io/errors v1.0.0-beta.7 h1:gypHW76pTQGVnHKo6QBkb4yFOJjC+sUGRc5Al3Odj1w=
cosmossdk.io/errors v1.0.0-beta.7/go.mod h1:mz6FQMJRku4bY7aqS/Gwfcmr/ue91roMEKAmDUDpBfE=
cosmossdk.io/math v1.0.0-beta.2 h1:17hSVc9ne1c31IaLDfjRojtN+y4Rd2N8H/6Fht2sBzw=
cosmossdk.io/math v1.0.0-beta.2/go.mod h1:u/MXvf8wbUbCsAEyQSSYXXMsczAsFX48e2D6JI86T4o=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmGfU=
filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=

View File

@ -30,3 +30,5 @@ Ref: https://keepachangelog.com/en/1.0.0/
# Changelog
## [Unreleased]
* [#12634](https://github.com/cosmos/cosmos-sdk/pull/12634) Move `sdk.Dec` to math package, call it `LegacyDec`.

View File

@ -1,4 +1,4 @@
package types
package math
import (
"encoding/json"
@ -10,26 +10,24 @@ import (
"testing"
)
var _ CustomProtobufType = (*Dec)(nil)
// NOTE: never use new(Dec) or else we will panic unmarshalling into the
// nil embedded big.Int
type Dec struct {
type LegacyDec struct {
i *big.Int
}
const (
// number of decimal places
Precision = 18
LegacyPrecision = 18
// bits required to represent the above precision
// Ceiling[Log2[10^Precision - 1]]
DecimalPrecisionBits = 60
LegacyDecimalPrecisionBits = 60
// decimalTruncateBits is the minimum number of bits removed
// by a truncate operation. It is equal to
// Floor[Log2[10^Precision - 1]].
decimalTruncateBits = DecimalPrecisionBits - 1
decimalTruncateBits = LegacyDecimalPrecisionBits - 1
maxDecBitLen = MaxBitLen + decimalTruncateBits
@ -38,7 +36,7 @@ const (
)
var (
precisionReuse = new(big.Int).Exp(big.NewInt(10), big.NewInt(Precision), nil)
precisionReuse = new(big.Int).Exp(big.NewInt(10), big.NewInt(LegacyPrecision), nil)
fivePrecision = new(big.Int).Quo(precisionReuse, big.NewInt(2))
precisionMultipliers []*big.Int
zeroInt = big.NewInt(0)
@ -48,15 +46,15 @@ var (
// Decimal errors
var (
ErrEmptyDecimalStr = errors.New("decimal string cannot be empty")
ErrInvalidDecimalLength = errors.New("invalid decimal length")
ErrInvalidDecimalStr = errors.New("invalid decimal string")
ErrLegacyEmptyDecimalStr = errors.New("decimal string cannot be empty")
ErrLegacyInvalidDecimalLength = errors.New("invalid decimal length")
ErrLegacyInvalidDecimalStr = errors.New("invalid decimal string")
)
// Set precision multipliers
func init() {
precisionMultipliers = make([]*big.Int, Precision+1)
for i := 0; i <= Precision; i++ {
precisionMultipliers = make([]*big.Int, LegacyPrecision+1)
for i := 0; i <= LegacyPrecision; i++ {
precisionMultipliers[i] = calcPrecisionMultiplier(int64(i))
}
}
@ -65,65 +63,65 @@ func precisionInt() *big.Int {
return new(big.Int).Set(precisionReuse)
}
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)} }
func LegacyZeroDec() LegacyDec { return LegacyDec{new(big.Int).Set(zeroInt)} }
func LegacyOneDec() LegacyDec { return LegacyDec{precisionInt()} }
func LegacySmallestDec() LegacyDec { return LegacyDec{new(big.Int).Set(oneInt)} }
// 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))
if prec > LegacyPrecision {
panic(fmt.Sprintf("too much precision, maximum %v, provided %v", LegacyPrecision, prec))
}
zerosToAdd := Precision - prec
zerosToAdd := LegacyPrecision - 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))
if prec > LegacyPrecision {
panic(fmt.Sprintf("too much precision, maximum %v, provided %v", LegacyPrecision, prec))
}
return precisionMultipliers[prec]
}
// create a new Dec from integer assuming whole number
func NewDec(i int64) Dec {
return NewDecWithPrec(i, 0)
func LegacyNewDec(i int64) LegacyDec {
return LegacyNewDecWithPrec(i, 0)
}
// create a new Dec from integer with decimal place at prec
// CONTRACT: prec <= Precision
func NewDecWithPrec(i, prec int64) Dec {
return Dec{
func LegacyNewDecWithPrec(i, prec int64) LegacyDec {
return LegacyDec{
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)
func LegacyNewDecFromBigInt(i *big.Int) LegacyDec {
return LegacyNewDecFromBigIntWithPrec(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{
func LegacyNewDecFromBigIntWithPrec(i *big.Int, prec int64) LegacyDec {
return LegacyDec{
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)
func LegacyNewDecFromInt(i Int) LegacyDec {
return LegacyNewDecFromIntWithPrec(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{
func LegacyNewDecFromIntWithPrec(i Int, prec int64) LegacyDec {
return LegacyDec{
new(big.Int).Mul(i.BigInt(), precisionMultiplier(prec)),
}
}
@ -141,9 +139,9 @@ func NewDecFromIntWithPrec(i Int, prec int64) Dec {
// are provided in the string than the constant Precision.
//
// CONTRACT - This function does not mutate the input str.
func NewDecFromStr(str string) (Dec, error) {
func LegacyNewDecFromStr(str string) (LegacyDec, error) {
if len(str) == 0 {
return Dec{}, fmt.Errorf("%s: %w", str, ErrEmptyDecimalStr)
return LegacyDec{}, fmt.Errorf("%s: %w", str, ErrLegacyEmptyDecimalStr)
}
// first extract any negative symbol
@ -154,7 +152,7 @@ func NewDecFromStr(str string) (Dec, error) {
}
if len(str) == 0 {
return Dec{}, fmt.Errorf("%s: %w", str, ErrEmptyDecimalStr)
return LegacyDec{}, fmt.Errorf("%s: %w", str, ErrLegacyEmptyDecimalStr)
}
strs := strings.Split(str, ".")
@ -164,62 +162,62 @@ func NewDecFromStr(str string) (Dec, error) {
if len(strs) == 2 { // has a decimal place
lenDecs = len(strs[1])
if lenDecs == 0 || len(combinedStr) == 0 {
return Dec{}, ErrInvalidDecimalLength
return LegacyDec{}, ErrLegacyInvalidDecimalLength
}
combinedStr += strs[1]
} else if len(strs) > 2 {
return Dec{}, ErrInvalidDecimalStr
return LegacyDec{}, ErrLegacyInvalidDecimalStr
}
if lenDecs > Precision {
return Dec{}, fmt.Errorf("value '%s' exceeds max precision by %d decimal places: max precision %d", str, Precision-lenDecs, Precision)
if lenDecs > LegacyPrecision {
return LegacyDec{}, fmt.Errorf("value '%s' exceeds max precision by %d decimal places: max precision %d", str, LegacyPrecision-lenDecs, LegacyPrecision)
}
// add some extra zero's to correct to the Precision factor
zerosToAdd := Precision - lenDecs
zerosToAdd := LegacyPrecision - lenDecs
zeros := fmt.Sprintf(`%0`+strconv.Itoa(zerosToAdd)+`s`, "")
combinedStr += zeros
combined, ok := new(big.Int).SetString(combinedStr, 10) // base 10
if !ok {
return Dec{}, fmt.Errorf("failed to set decimal string with base 10: %s", combinedStr)
return LegacyDec{}, fmt.Errorf("failed to set decimal string with base 10: %s", combinedStr)
}
if combined.BitLen() > maxDecBitLen {
return Dec{}, fmt.Errorf("decimal '%s' out of range; bitLen: got %d, max %d", str, combined.BitLen(), maxDecBitLen)
return LegacyDec{}, fmt.Errorf("decimal '%s' out of range; bitLen: got %d, max %d", str, combined.BitLen(), maxDecBitLen)
}
if neg {
combined = new(big.Int).Neg(combined)
}
return Dec{combined}, nil
return LegacyDec{combined}, nil
}
// Decimal from string, panic on error
func MustNewDecFromStr(s string) Dec {
dec, err := NewDecFromStr(s)
func LegacyMustNewDecFromStr(s string) LegacyDec {
dec, err := LegacyNewDecFromStr(s)
if err != nil {
panic(err)
}
return dec
}
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
func (d Dec) NegMut() Dec { d.i.Neg(d.i); return d } // reverse the decimal sign, mutable
func (d Dec) Abs() Dec { return Dec{new(big.Int).Abs(d.i)} } // absolute value
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
func (d LegacyDec) IsNil() bool { return d.i == nil } // is decimal nil
func (d LegacyDec) IsZero() bool { return (d.i).Sign() == 0 } // is equal to zero
func (d LegacyDec) IsNegative() bool { return (d.i).Sign() == -1 } // is negative
func (d LegacyDec) IsPositive() bool { return (d.i).Sign() == 1 } // is positive
func (d LegacyDec) Equal(d2 LegacyDec) bool { return (d.i).Cmp(d2.i) == 0 } // equal decimals
func (d LegacyDec) GT(d2 LegacyDec) bool { return (d.i).Cmp(d2.i) > 0 } // greater than
func (d LegacyDec) GTE(d2 LegacyDec) bool { return (d.i).Cmp(d2.i) >= 0 } // greater than or equal
func (d LegacyDec) LT(d2 LegacyDec) bool { return (d.i).Cmp(d2.i) < 0 } // less than
func (d LegacyDec) LTE(d2 LegacyDec) bool { return (d.i).Cmp(d2.i) <= 0 } // less than or equal
func (d LegacyDec) Neg() LegacyDec { return LegacyDec{new(big.Int).Neg(d.i)} } // reverse the decimal sign
func (d LegacyDec) NegMut() LegacyDec { d.i.Neg(d.i); return d } // reverse the decimal sign, mutable
func (d LegacyDec) Abs() LegacyDec { return LegacyDec{new(big.Int).Abs(d.i)} } // absolute value
func (d LegacyDec) Set(d2 LegacyDec) LegacyDec { d.i.Set(d2.i); return d } // set to existing dec value
func (d LegacyDec) Clone() LegacyDec { return LegacyDec{new(big.Int).Set(d.i)} } // clone new dec
// BigInt returns a copy of the underlying big.Int.
func (d Dec) BigInt() *big.Int {
func (d LegacyDec) BigInt() *big.Int {
if d.IsNil() {
return nil
}
@ -228,34 +226,34 @@ func (d Dec) BigInt() *big.Int {
return cp.Set(d.i)
}
func (d Dec) ImmutOp(op func(Dec, Dec) Dec, d2 Dec) Dec {
func (d LegacyDec) ImmutOp(op func(LegacyDec, LegacyDec) LegacyDec, d2 LegacyDec) LegacyDec {
return op(d.Clone(), d2)
}
func (d Dec) ImmutOpInt(op func(Dec, Int) Dec, d2 Int) Dec {
func (d LegacyDec) ImmutOpInt(op func(LegacyDec, Int) LegacyDec, d2 Int) LegacyDec {
return op(d.Clone(), d2)
}
func (d Dec) ImmutOpInt64(op func(Dec, int64) Dec, d2 int64) Dec {
func (d LegacyDec) ImmutOpInt64(op func(LegacyDec, int64) LegacyDec, d2 int64) LegacyDec {
// 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 {
func (d LegacyDec) SetInt64(i int64) LegacyDec {
d.i.SetInt64(i)
d.i.Mul(d.i, precisionReuse)
return d
}
// addition
func (d Dec) Add(d2 Dec) Dec {
return d.ImmutOp(Dec.AddMut, d2)
func (d LegacyDec) Add(d2 LegacyDec) LegacyDec {
return d.ImmutOp(LegacyDec.AddMut, d2)
}
// mutable addition
func (d Dec) AddMut(d2 Dec) Dec {
func (d LegacyDec) AddMut(d2 LegacyDec) LegacyDec {
d.i.Add(d.i, d2.i)
if d.i.BitLen() > maxDecBitLen {
@ -265,12 +263,12 @@ func (d Dec) AddMut(d2 Dec) Dec {
}
// subtraction
func (d Dec) Sub(d2 Dec) Dec {
return d.ImmutOp(Dec.SubMut, d2)
func (d LegacyDec) Sub(d2 LegacyDec) LegacyDec {
return d.ImmutOp(LegacyDec.SubMut, d2)
}
// mutable subtraction
func (d Dec) SubMut(d2 Dec) Dec {
func (d LegacyDec) SubMut(d2 LegacyDec) LegacyDec {
d.i.Sub(d.i, d2.i)
if d.i.BitLen() > maxDecBitLen {
@ -280,12 +278,12 @@ func (d Dec) SubMut(d2 Dec) Dec {
}
// multiplication
func (d Dec) Mul(d2 Dec) Dec {
return d.ImmutOp(Dec.MulMut, d2)
func (d LegacyDec) Mul(d2 LegacyDec) LegacyDec {
return d.ImmutOp(LegacyDec.MulMut, d2)
}
// mutable multiplication
func (d Dec) MulMut(d2 Dec) Dec {
func (d LegacyDec) MulMut(d2 LegacyDec) LegacyDec {
d.i.Mul(d.i, d2.i)
chopped := chopPrecisionAndRound(d.i)
@ -297,12 +295,12 @@ func (d Dec) MulMut(d2 Dec) Dec {
}
// multiplication truncate
func (d Dec) MulTruncate(d2 Dec) Dec {
return d.ImmutOp(Dec.MulTruncateMut, d2)
func (d LegacyDec) MulTruncate(d2 LegacyDec) LegacyDec {
return d.ImmutOp(LegacyDec.MulTruncateMut, d2)
}
// mutable multiplication truncage
func (d Dec) MulTruncateMut(d2 Dec) Dec {
func (d LegacyDec) MulTruncateMut(d2 LegacyDec) LegacyDec {
d.i.Mul(d.i, d2.i)
chopPrecisionAndTruncate(d.i)
@ -313,11 +311,11 @@ func (d Dec) MulTruncateMut(d2 Dec) Dec {
}
// multiplication
func (d Dec) MulInt(i Int) Dec {
return d.ImmutOpInt(Dec.MulIntMut, i)
func (d LegacyDec) MulInt(i Int) LegacyDec {
return d.ImmutOpInt(LegacyDec.MulIntMut, i)
}
func (d Dec) MulIntMut(i Int) Dec {
func (d LegacyDec) MulIntMut(i Int) LegacyDec {
d.i.Mul(d.i, i.BigInt())
if d.i.BitLen() > maxDecBitLen {
panic("Int overflow")
@ -326,11 +324,11 @@ func (d Dec) MulIntMut(i Int) Dec {
}
// MulInt64 - multiplication with int64
func (d Dec) MulInt64(i int64) Dec {
return d.ImmutOpInt64(Dec.MulInt64Mut, i)
func (d LegacyDec) MulInt64(i int64) LegacyDec {
return d.ImmutOpInt64(LegacyDec.MulInt64Mut, i)
}
func (d Dec) MulInt64Mut(i int64) Dec {
func (d LegacyDec) MulInt64Mut(i int64) LegacyDec {
d.i.Mul(d.i, big.NewInt(i))
if d.i.BitLen() > maxDecBitLen {
@ -340,12 +338,12 @@ func (d Dec) MulInt64Mut(i int64) Dec {
}
// quotient
func (d Dec) Quo(d2 Dec) Dec {
return d.ImmutOp(Dec.QuoMut, d2)
func (d LegacyDec) Quo(d2 LegacyDec) LegacyDec {
return d.ImmutOp(LegacyDec.QuoMut, d2)
}
// mutable quotient
func (d Dec) QuoMut(d2 Dec) Dec {
func (d LegacyDec) QuoMut(d2 LegacyDec) LegacyDec {
// multiply precision twice
d.i.Mul(d.i, precisionReuse)
d.i.Mul(d.i, precisionReuse)
@ -359,12 +357,12 @@ func (d Dec) QuoMut(d2 Dec) Dec {
}
// quotient truncate
func (d Dec) QuoTruncate(d2 Dec) Dec {
return d.ImmutOp(Dec.QuoTruncateMut, d2)
func (d LegacyDec) QuoTruncate(d2 LegacyDec) LegacyDec {
return d.ImmutOp(LegacyDec.QuoTruncateMut, d2)
}
// mutable quotient truncate
func (d Dec) QuoTruncateMut(d2 Dec) Dec {
func (d LegacyDec) QuoTruncateMut(d2 LegacyDec) LegacyDec {
// multiply precision twice
d.i.Mul(d.i, precisionReuse)
d.i.Mul(d.i, precisionReuse)
@ -378,12 +376,12 @@ func (d Dec) QuoTruncateMut(d2 Dec) Dec {
}
// quotient, round up
func (d Dec) QuoRoundUp(d2 Dec) Dec {
return d.ImmutOp(Dec.QuoRoundupMut, d2)
func (d LegacyDec) QuoRoundUp(d2 LegacyDec) LegacyDec {
return d.ImmutOp(LegacyDec.QuoRoundupMut, d2)
}
// mutable quotient, round up
func (d Dec) QuoRoundupMut(d2 Dec) Dec {
func (d LegacyDec) QuoRoundupMut(d2 LegacyDec) LegacyDec {
// multiply precision twice
d.i.Mul(d.i, precisionReuse)
d.i.Mul(d.i, precisionReuse)
@ -397,21 +395,21 @@ func (d Dec) QuoRoundupMut(d2 Dec) Dec {
}
// quotient
func (d Dec) QuoInt(i Int) Dec {
return d.ImmutOpInt(Dec.QuoIntMut, i)
func (d LegacyDec) QuoInt(i Int) LegacyDec {
return d.ImmutOpInt(LegacyDec.QuoIntMut, i)
}
func (d Dec) QuoIntMut(i Int) Dec {
func (d LegacyDec) QuoIntMut(i Int) LegacyDec {
d.i.Quo(d.i, i.BigInt())
return d
}
// QuoInt64 - quotient with int64
func (d Dec) QuoInt64(i int64) Dec {
return d.ImmutOpInt64(Dec.QuoInt64Mut, i)
func (d LegacyDec) QuoInt64(i int64) LegacyDec {
return d.ImmutOpInt64(LegacyDec.QuoInt64Mut, i)
}
func (d Dec) QuoInt64Mut(i int64) Dec {
func (d LegacyDec) QuoInt64Mut(i int64) LegacyDec {
d.i.Quo(d.i, big.NewInt(i))
return d
}
@ -422,7 +420,7 @@ func (d Dec) QuoInt64Mut(i int64) Dec {
// approximate answer. It returns `|d|.ApproxRoot() * -1` if input is negative.
// 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.
func (d Dec) ApproxRoot(root uint64) (guess Dec, err error) {
func (d LegacyDec) ApproxRoot(root uint64) (guess LegacyDec, err error) {
defer func() {
if r := recover(); r != nil {
var ok bool
@ -438,20 +436,20 @@ func (d Dec) ApproxRoot(root uint64) (guess Dec, err error) {
return absRoot.NegMut(), err
}
if root == 1 || d.IsZero() || d.Equal(OneDec()) {
if root == 1 || d.IsZero() || d.Equal(LegacyOneDec()) {
return d, nil
}
if root == 0 {
return OneDec(), nil
return LegacyOneDec(), nil
}
guess, delta := OneDec(), OneDec()
guess, delta := LegacyOneDec(), LegacyOneDec()
for iter := 0; delta.Abs().GT(SmallestDec()) && iter < maxApproxRootIterations; iter++ {
for iter := 0; delta.Abs().GT(LegacySmallestDec()) && iter < maxApproxRootIterations; iter++ {
prev := guess.Power(root - 1)
if prev.IsZero() {
prev = SmallestDec()
prev = LegacySmallestDec()
}
delta.Set(d).QuoMut(prev)
delta.SubMut(guess)
@ -464,17 +462,17 @@ func (d Dec) ApproxRoot(root uint64) (guess Dec, err error) {
}
// Power returns a the result of raising to a positive integer power
func (d Dec) Power(power uint64) Dec {
res := Dec{new(big.Int).Set(d.i)}
func (d LegacyDec) Power(power uint64) LegacyDec {
res := LegacyDec{new(big.Int).Set(d.i)}
return res.PowerMut(power)
}
func (d Dec) PowerMut(power uint64) Dec {
func (d LegacyDec) PowerMut(power uint64) LegacyDec {
if power == 0 {
d.SetInt64(1)
return d
}
tmp := OneDec()
tmp := LegacyOneDec()
for i := power; i > 1; {
if i%2 != 0 {
@ -489,24 +487,24 @@ func (d Dec) PowerMut(power uint64) Dec {
// 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) {
func (d LegacyDec) ApproxSqrt() (LegacyDec, error) {
return d.ApproxRoot(2)
}
// is integer, e.g. decimals are zero
func (d Dec) IsInteger() bool {
func (d LegacyDec) IsInteger() bool {
return new(big.Int).Rem(d.i, precisionReuse).Sign() == 0
}
// format decimal state
func (d Dec) Format(s fmt.State, verb rune) {
func (d LegacyDec) Format(s fmt.State, verb rune) {
_, err := s.Write([]byte(d.String()))
if err != nil {
panic(err)
}
}
func (d Dec) String() string {
func (d LegacyDec) String() string {
if d.i == nil {
return d.i.String()
}
@ -527,24 +525,24 @@ func (d Dec) String() string {
// TODO: Remove trailing zeros
// case 1, purely decimal
if inputSize <= Precision {
bzStr = make([]byte, Precision+2)
if inputSize <= LegacyPrecision {
bzStr = make([]byte, LegacyPrecision+2)
// 0. prefix
bzStr[0] = byte('0')
bzStr[1] = byte('.')
// set relevant digits to 0
for i := 0; i < Precision-inputSize; i++ {
for i := 0; i < LegacyPrecision-inputSize; i++ {
bzStr[i+2] = byte('0')
}
// set final digits
copy(bzStr[2+(Precision-inputSize):], bzInt)
copy(bzStr[2+(LegacyPrecision-inputSize):], bzInt)
} else {
// inputSize + 1 to account for the decimal point that is being added
bzStr = make([]byte, inputSize+1)
decPointPlace := inputSize - Precision
decPointPlace := inputSize - LegacyPrecision
copy(bzStr, bzInt[:decPointPlace]) // pre-decimal digits
bzStr[decPointPlace] = byte('.') // decimal point
@ -560,13 +558,13 @@ func (d Dec) String() string {
// Float64 returns the float64 representation of a Dec.
// Will return the error if the conversion failed.
func (d Dec) Float64() (float64, error) {
func (d LegacyDec) 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 {
func (d LegacyDec) MustFloat64() float64 {
if value, err := strconv.ParseFloat(d.String(), 64); err != nil {
panic(err)
} else {
@ -647,7 +645,7 @@ func chopPrecisionAndRoundNonMutative(d *big.Int) *big.Int {
}
// RoundInt64 rounds the decimal using bankers rounding
func (d Dec) RoundInt64() int64 {
func (d LegacyDec) RoundInt64() int64 {
chopped := chopPrecisionAndRoundNonMutative(d.i)
if !chopped.IsInt64() {
panic("Int64() out of bound")
@ -656,7 +654,7 @@ func (d Dec) RoundInt64() int64 {
}
// RoundInt round the decimal using bankers rounding
func (d Dec) RoundInt() Int {
func (d LegacyDec) RoundInt() Int {
return NewIntFromBigInt(chopPrecisionAndRoundNonMutative(d.i))
}
@ -673,7 +671,7 @@ func chopPrecisionAndTruncateNonMutative(d *big.Int) *big.Int {
}
// TruncateInt64 truncates the decimals from the number and returns an int64
func (d Dec) TruncateInt64() int64 {
func (d LegacyDec) TruncateInt64() int64 {
chopped := chopPrecisionAndTruncateNonMutative(d.i)
if !chopped.IsInt64() {
panic("Int64() out of bound")
@ -682,18 +680,18 @@ func (d Dec) TruncateInt64() int64 {
}
// TruncateInt truncates the decimals from the number and returns an Int
func (d Dec) TruncateInt() Int {
func (d LegacyDec) TruncateInt() Int {
return NewIntFromBigInt(chopPrecisionAndTruncateNonMutative(d.i))
}
// TruncateDec truncates the decimals from the number and returns a Dec
func (d Dec) TruncateDec() Dec {
return NewDecFromBigInt(chopPrecisionAndTruncateNonMutative(d.i))
func (d LegacyDec) TruncateDec() LegacyDec {
return LegacyNewDecFromBigInt(chopPrecisionAndTruncateNonMutative(d.i))
}
// Ceil returns the smallest interger value (as a decimal) that is greater than
// or equal to the given decimal.
func (d Dec) Ceil() Dec {
func (d LegacyDec) Ceil() LegacyDec {
tmp := new(big.Int).Set(d.i)
quo, rem := tmp, big.NewInt(0)
@ -701,52 +699,52 @@ func (d Dec) Ceil() Dec {
// no need to round with a zero remainder regardless of sign
if rem.Cmp(zeroInt) == 0 {
return NewDecFromBigInt(quo)
return LegacyNewDecFromBigInt(quo)
}
if rem.Sign() == -1 {
return NewDecFromBigInt(quo)
return LegacyNewDecFromBigInt(quo)
}
return NewDecFromBigInt(quo.Add(quo, oneInt))
return LegacyNewDecFromBigInt(quo.Add(quo, oneInt))
}
// MaxSortableDec is the largest Dec that can be passed into SortableDecBytes()
// Its negative form is the least Dec that can be passed in.
var MaxSortableDec Dec
var LegacyMaxSortableDec LegacyDec
func init() {
MaxSortableDec = OneDec().Quo(SmallestDec())
LegacyMaxSortableDec = LegacyOneDec().Quo(LegacySmallestDec())
}
// 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)
func LegacyValidSortableDec(dec LegacyDec) bool {
return dec.Abs().LTE(LegacyMaxSortableDec)
}
// 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) {
func LegacySortableDecBytes(dec LegacyDec) []byte {
if !LegacyValidSortableDec(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) {
if dec.Equal(LegacyMaxSortableDec) {
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()) {
if dec.Equal(LegacyMaxSortableDec.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 append([]byte("-"), []byte(fmt.Sprintf(fmt.Sprintf("%%0%ds", LegacyPrecision*2+1), dec.Abs().String()))...)
}
return []byte(fmt.Sprintf(fmt.Sprintf("%%0%ds", Precision*2+1), dec.String()))
return []byte(fmt.Sprintf(fmt.Sprintf("%%0%ds", LegacyPrecision*2+1), dec.String()))
}
// reuse nil values
@ -759,7 +757,7 @@ func init() {
}
// MarshalJSON marshals the decimal
func (d Dec) MarshalJSON() ([]byte, error) {
func (d LegacyDec) MarshalJSON() ([]byte, error) {
if d.i == nil {
return nilJSON, nil
}
@ -767,7 +765,7 @@ func (d Dec) MarshalJSON() ([]byte, error) {
}
// UnmarshalJSON defines custom decoding scheme
func (d *Dec) UnmarshalJSON(bz []byte) error {
func (d *LegacyDec) UnmarshalJSON(bz []byte) error {
if d.i == nil {
d.i = new(big.Int)
}
@ -779,7 +777,7 @@ func (d *Dec) UnmarshalJSON(bz []byte) error {
}
// TODO: Reuse dec allocation
newDec, err := NewDecFromStr(text)
newDec, err := LegacyNewDecFromStr(text)
if err != nil {
return err
}
@ -789,12 +787,12 @@ func (d *Dec) UnmarshalJSON(bz []byte) error {
}
// MarshalYAML returns the YAML representation.
func (d Dec) MarshalYAML() (interface{}, error) {
func (d LegacyDec) MarshalYAML() (interface{}, error) {
return d.String(), nil
}
// Marshal implements the gogo proto custom type interface.
func (d Dec) Marshal() ([]byte, error) {
func (d LegacyDec) Marshal() ([]byte, error) {
if d.i == nil {
d.i = new(big.Int)
}
@ -802,7 +800,7 @@ func (d Dec) Marshal() ([]byte, error) {
}
// MarshalTo implements the gogo proto custom type interface.
func (d *Dec) MarshalTo(data []byte) (n int, err error) {
func (d *LegacyDec) MarshalTo(data []byte) (n int, err error) {
if d.i == nil {
d.i = new(big.Int)
}
@ -822,7 +820,7 @@ func (d *Dec) MarshalTo(data []byte) (n int, err error) {
}
// Unmarshal implements the gogo proto custom type interface.
func (d *Dec) Unmarshal(data []byte) error {
func (d *LegacyDec) Unmarshal(data []byte) error {
if len(data) == 0 {
d = nil
return nil
@ -844,23 +842,19 @@ func (d *Dec) Unmarshal(data []byte) error {
}
// Size implements the gogo proto custom type interface.
func (d *Dec) Size() int {
func (d *LegacyDec) 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) }
func (dp DecProto) String() string {
return dp.Dec.String()
}
func (d LegacyDec) MarshalAmino() ([]byte, error) { return d.Marshal() }
func (d *LegacyDec) UnmarshalAmino(bz []byte) error { return d.Unmarshal(bz) }
// helpers
// test if two decimal arrays are equal
func DecsEqual(d1s, d2s []Dec) bool {
func LegacyDecsEqual(d1s, d2s []LegacyDec) bool {
if len(d1s) != len(d2s) {
return false
}
@ -874,7 +868,7 @@ func DecsEqual(d1s, d2s []Dec) bool {
}
// minimum decimal between two
func MinDec(d1, d2 Dec) Dec {
func LegacyMinDec(d1, d2 LegacyDec) LegacyDec {
if d1.LT(d2) {
return d1
}
@ -882,7 +876,7 @@ func MinDec(d1, d2 Dec) Dec {
}
// maximum decimal between two
func MaxDec(d1, d2 Dec) Dec {
func LegacyMaxDec(d1, d2 LegacyDec) LegacyDec {
if d1.LT(d2) {
return d2
}
@ -890,11 +884,11 @@ func MaxDec(d1, d2 Dec) Dec {
}
// intended to be used with require/assert: require.True(DecEq(...))
func DecEq(t *testing.T, exp, got Dec) (*testing.T, bool, string, string, string) {
func LegacyDecEq(t *testing.T, exp, got LegacyDec) (*testing.T, bool, string, string, string) {
return t, exp.Equal(got), "expected:\t%v\ngot:\t\t%v", exp.String(), got.String()
}
func DecApproxEq(t *testing.T, d1 Dec, d2 Dec, tol Dec) (*testing.T, bool, string, string, string) {
func LegacyDecApproxEq(t *testing.T, d1 LegacyDec, d2 LegacyDec, tol LegacyDec) (*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()
}

View File

@ -1,6 +1,7 @@
package types
package math
import (
"encoding/json"
"math/big"
"testing"
@ -22,34 +23,34 @@ func (s *decimalInternalTestSuite) TestPrecisionMultiplier() {
}
func (s *decimalInternalTestSuite) TestZeroDeserializationJSON() {
d := Dec{new(big.Int)}
err := cdc.UnmarshalJSON([]byte(`"0"`), &d)
d := LegacyDec{new(big.Int)}
err := json.Unmarshal([]byte(`"0"`), &d)
s.Require().Nil(err)
err = cdc.UnmarshalJSON([]byte(`"{}"`), &d)
err = json.Unmarshal([]byte(`"{}"`), &d)
s.Require().NotNil(err)
}
func (s *decimalInternalTestSuite) TestSerializationGocodecJSON() {
d := MustNewDecFromStr("0.333")
d := LegacyMustNewDecFromStr("0.333")
bz, err := cdc.MarshalJSON(d)
bz, err := json.Marshal(d)
s.Require().NoError(err)
d2 := Dec{new(big.Int)}
err = cdc.UnmarshalJSON(bz, &d2)
d2 := LegacyDec{new(big.Int)}
err = json.Unmarshal(bz, &d2)
s.Require().NoError(err)
s.Require().True(d.Equal(d2), "original: %v, unmarshalled: %v", d, d2)
}
func (s *decimalInternalTestSuite) TestDecMarshalJSON() {
decimal := func(i int64) Dec {
d := NewDec(0)
decimal := func(i int64) LegacyDec {
d := LegacyNewDec(0)
d.i = new(big.Int).SetInt64(i)
return d
}
tests := []struct {
name string
d Dec
d LegacyDec
want string
wantErr bool // if wantErr = false, will also attempt unmarshaling
}{
@ -57,10 +58,10 @@ func (s *decimalInternalTestSuite) TestDecMarshalJSON() {
{"one", decimal(1), "\"0.000000000000000001\"", false},
{"ten", decimal(10), "\"0.000000000000000010\"", false},
{"12340", decimal(12340), "\"0.000000000000012340\"", false},
{"zeroInt", NewDec(0), "\"0.000000000000000000\"", false},
{"oneInt", NewDec(1), "\"1.000000000000000000\"", false},
{"tenInt", NewDec(10), "\"10.000000000000000000\"", false},
{"12340Int", NewDec(12340), "\"12340.000000000000000000\"", false},
{"zeroInt", LegacyNewDec(0), "\"0.000000000000000000\"", false},
{"oneInt", LegacyNewDec(1), "\"1.000000000000000000\"", false},
{"tenInt", LegacyNewDec(10), "\"10.000000000000000000\"", false},
{"12340Int", LegacyNewDec(12340), "\"12340.000000000000000000\"", false},
}
for _, tt := range tests {
tt := tt
@ -72,7 +73,7 @@ func (s *decimalInternalTestSuite) TestDecMarshalJSON() {
}
if !tt.wantErr {
s.Require().Equal(tt.want, string(got), "incorrect marshalled value")
unmarshalledDec := NewDec(0)
unmarshalledDec := LegacyNewDec(0)
err := unmarshalledDec.UnmarshalJSON(got)
s.Require().NoError(err)
s.Require().Equal(tt.d, unmarshalledDec, "incorrect unmarshalled value")

621
math/dec_test.go Normal file
View File

@ -0,0 +1,621 @@
package math_test
import (
"bytes"
"encoding/json"
"fmt"
"math/big"
"strings"
"testing"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"sigs.k8s.io/yaml"
"cosmossdk.io/math"
)
type decimalTestSuite struct {
suite.Suite
}
func TestDecimalTestSuite(t *testing.T) {
suite.Run(t, new(decimalTestSuite))
}
func TestDecApproxEq(t *testing.T) {
// d1 = 0.55, d2 = 0.6, tol = 0.1
d1 := math.LegacyNewDecWithPrec(55, 2)
d2 := math.LegacyNewDecWithPrec(6, 1)
tol := math.LegacyNewDecWithPrec(1, 1)
require.True(math.LegacyDecApproxEq(t, d1, d2, tol))
// d1 = 0.55, d2 = 0.6, tol = 1E-5
d1 = math.LegacyNewDecWithPrec(55, 2)
d2 = math.LegacyNewDecWithPrec(6, 1)
tol = math.LegacyNewDecWithPrec(1, 5)
require.False(math.LegacyDecApproxEq(t, d1, d2, tol))
// d1 = 0.6, d2 = 0.61, tol = 0.01
d1 = math.LegacyNewDecWithPrec(6, 1)
d2 = math.LegacyNewDecWithPrec(61, 2)
tol = math.LegacyNewDecWithPrec(1, 2)
require.True(math.LegacyDecApproxEq(t, d1, d2, tol))
}
// create a decimal from a decimal string (ex. "1234.5678")
func (s *decimalTestSuite) mustNewDecFromStr(str string) (d math.LegacyDec) {
d, err := math.LegacyNewDecFromStr(str)
s.Require().NoError(err)
return d
}
func (s *decimalTestSuite) TestNewDecFromStr() {
largeBigInt, ok := new(big.Int).SetString("3144605511029693144278234343371835", 10)
s.Require().True(ok)
largerBigInt, ok := new(big.Int).SetString("8888888888888888888888888888888888888888888888888888888888888888888844444440", 10)
s.Require().True(ok)
largestBigInt, ok := new(big.Int).SetString("33499189745056880149688856635597007162669032647290798121690100488888732861290034376435130433535", 10)
s.Require().True(ok)
tests := []struct {
decimalStr string
expErr bool
exp math.LegacyDec
}{
{"", true, math.LegacyDec{}},
{"0.-75", true, math.LegacyDec{}},
{"0", false, math.LegacyNewDec(0)},
{"1", false, math.LegacyNewDec(1)},
{"1.1", false, math.LegacyNewDecWithPrec(11, 1)},
{"0.75", false, math.LegacyNewDecWithPrec(75, 2)},
{"0.8", false, math.LegacyNewDecWithPrec(8, 1)},
{"0.11111", false, math.LegacyNewDecWithPrec(11111, 5)},
{"314460551102969.3144278234343371835", true, math.LegacyNewDec(3141203149163817869)},
{
"314460551102969314427823434337.1835718092488231350",
true, math.LegacyNewDecFromBigIntWithPrec(largeBigInt, 4),
},
{
"314460551102969314427823434337.1835",
false, math.LegacyNewDecFromBigIntWithPrec(largeBigInt, 4),
},
{".", true, math.LegacyDec{}},
{".0", true, math.LegacyNewDec(0)},
{"1.", true, math.LegacyNewDec(1)},
{"foobar", true, math.LegacyDec{}},
{"0.foobar", true, math.LegacyDec{}},
{"0.foobar.", true, math.LegacyDec{}},
{"8888888888888888888888888888888888888888888888888888888888888888888844444440", false, math.LegacyNewDecFromBigInt(largerBigInt)},
{"33499189745056880149688856635597007162669032647290798121690100488888732861290.034376435130433535", false, math.LegacyNewDecFromBigIntWithPrec(largestBigInt, 18)},
{"133499189745056880149688856635597007162669032647290798121690100488888732861291", true, math.LegacyDec{}},
}
for tcIndex, tc := range tests {
res, err := math.LegacyNewDecFromStr(tc.decimalStr)
if tc.expErr {
s.Require().NotNil(err, "error expected, decimalStr %v, tc %v", tc.decimalStr, tcIndex)
} else {
s.Require().Nil(err, "unexpected error, decimalStr %v, tc %v", tc.decimalStr, tcIndex)
s.Require().True(res.Equal(tc.exp), "equality was incorrect, res %v, exp %v, tc %v", res, tc.exp, tcIndex)
}
// negative tc
res, err = math.LegacyNewDecFromStr("-" + tc.decimalStr)
if tc.expErr {
s.Require().NotNil(err, "error expected, decimalStr %v, tc %v", tc.decimalStr, tcIndex)
} else {
s.Require().Nil(err, "unexpected error, decimalStr %v, tc %v", tc.decimalStr, tcIndex)
exp := tc.exp.Mul(math.LegacyNewDec(-1))
s.Require().True(res.Equal(exp), "equality was incorrect, res %v, exp %v, tc %v", res, exp, tcIndex)
}
}
}
func (s *decimalTestSuite) TestDecString() {
tests := []struct {
d math.LegacyDec
want string
}{
{math.LegacyNewDec(0), "0.000000000000000000"},
{math.LegacyNewDec(1), "1.000000000000000000"},
{math.LegacyNewDec(10), "10.000000000000000000"},
{math.LegacyNewDec(12340), "12340.000000000000000000"},
{math.LegacyNewDecWithPrec(12340, 4), "1.234000000000000000"},
{math.LegacyNewDecWithPrec(12340, 5), "0.123400000000000000"},
{math.LegacyNewDecWithPrec(12340, 8), "0.000123400000000000"},
{math.LegacyNewDecWithPrec(1009009009009009009, 17), "10.090090090090090090"},
}
for tcIndex, tc := range tests {
s.Require().Equal(tc.want, tc.d.String(), "bad String(), index: %v", tcIndex)
}
}
func (s *decimalTestSuite) TestDecFloat64() {
tests := []struct {
d math.LegacyDec
want float64
}{
{math.LegacyNewDec(0), 0.000000000000000000},
{math.LegacyNewDec(1), 1.000000000000000000},
{math.LegacyNewDec(10), 10.000000000000000000},
{math.LegacyNewDec(12340), 12340.000000000000000000},
{math.LegacyNewDecWithPrec(12340, 4), 1.234000000000000000},
{math.LegacyNewDecWithPrec(12340, 5), 0.123400000000000000},
{math.LegacyNewDecWithPrec(12340, 8), 0.000123400000000000},
{math.LegacyNewDecWithPrec(1009009009009009009, 17), 10.090090090090090090},
}
for tcIndex, tc := range tests {
value, err := tc.d.Float64()
s.Require().Nil(err, "error getting Float64(), index: %v", tcIndex)
s.Require().Equal(tc.want, value, "bad Float64(), index: %v", tcIndex)
s.Require().Equal(tc.want, tc.d.MustFloat64(), "bad MustFloat64(), index: %v", tcIndex)
}
}
func (s *decimalTestSuite) TestEqualities() {
tests := []struct {
d1, d2 math.LegacyDec
gt, lt, eq bool
}{
{math.LegacyNewDec(0), math.LegacyNewDec(0), false, false, true},
{math.LegacyNewDecWithPrec(0, 2), math.LegacyNewDecWithPrec(0, 4), false, false, true},
{math.LegacyNewDecWithPrec(100, 0), math.LegacyNewDecWithPrec(100, 0), false, false, true},
{math.LegacyNewDecWithPrec(-100, 0), math.LegacyNewDecWithPrec(-100, 0), false, false, true},
{math.LegacyNewDecWithPrec(-1, 1), math.LegacyNewDecWithPrec(-1, 1), false, false, true},
{math.LegacyNewDecWithPrec(3333, 3), math.LegacyNewDecWithPrec(3333, 3), false, false, true},
{math.LegacyNewDecWithPrec(0, 0), math.LegacyNewDecWithPrec(3333, 3), false, true, false},
{math.LegacyNewDecWithPrec(0, 0), math.LegacyNewDecWithPrec(100, 0), false, true, false},
{math.LegacyNewDecWithPrec(-1, 0), math.LegacyNewDecWithPrec(3333, 3), false, true, false},
{math.LegacyNewDecWithPrec(-1, 0), math.LegacyNewDecWithPrec(100, 0), false, true, false},
{math.LegacyNewDecWithPrec(1111, 3), math.LegacyNewDecWithPrec(100, 0), false, true, false},
{math.LegacyNewDecWithPrec(1111, 3), math.LegacyNewDecWithPrec(3333, 3), false, true, false},
{math.LegacyNewDecWithPrec(-3333, 3), math.LegacyNewDecWithPrec(-1111, 3), false, true, false},
{math.LegacyNewDecWithPrec(3333, 3), math.LegacyNewDecWithPrec(0, 0), true, false, false},
{math.LegacyNewDecWithPrec(100, 0), math.LegacyNewDecWithPrec(0, 0), true, false, false},
{math.LegacyNewDecWithPrec(3333, 3), math.LegacyNewDecWithPrec(-1, 0), true, false, false},
{math.LegacyNewDecWithPrec(100, 0), math.LegacyNewDecWithPrec(-1, 0), true, false, false},
{math.LegacyNewDecWithPrec(100, 0), math.LegacyNewDecWithPrec(1111, 3), true, false, false},
{math.LegacyNewDecWithPrec(3333, 3), math.LegacyNewDecWithPrec(1111, 3), true, false, false},
{math.LegacyNewDecWithPrec(-1111, 3), math.LegacyNewDecWithPrec(-3333, 3), true, false, false},
}
for tcIndex, tc := range tests {
s.Require().Equal(tc.gt, tc.d1.GT(tc.d2), "GT result is incorrect, tc %d", tcIndex)
s.Require().Equal(tc.lt, tc.d1.LT(tc.d2), "LT result is incorrect, tc %d", tcIndex)
s.Require().Equal(tc.eq, tc.d1.Equal(tc.d2), "equality result is incorrect, tc %d", tcIndex)
}
}
func (s *decimalTestSuite) TestDecsEqual() {
tests := []struct {
d1s, d2s []math.LegacyDec
eq bool
}{
{[]math.LegacyDec{math.LegacyNewDec(0)}, []math.LegacyDec{math.LegacyNewDec(0)}, true},
{[]math.LegacyDec{math.LegacyNewDec(0)}, []math.LegacyDec{math.LegacyNewDec(1)}, false},
{[]math.LegacyDec{math.LegacyNewDec(0)}, []math.LegacyDec{}, false},
{[]math.LegacyDec{math.LegacyNewDec(0), math.LegacyNewDec(1)}, []math.LegacyDec{math.LegacyNewDec(0), math.LegacyNewDec(1)}, true},
{[]math.LegacyDec{math.LegacyNewDec(1), math.LegacyNewDec(0)}, []math.LegacyDec{math.LegacyNewDec(1), math.LegacyNewDec(0)}, true},
{[]math.LegacyDec{math.LegacyNewDec(1), math.LegacyNewDec(0)}, []math.LegacyDec{math.LegacyNewDec(0), math.LegacyNewDec(1)}, false},
{[]math.LegacyDec{math.LegacyNewDec(1), math.LegacyNewDec(0)}, []math.LegacyDec{math.LegacyNewDec(1)}, false},
{[]math.LegacyDec{math.LegacyNewDec(1), math.LegacyNewDec(2)}, []math.LegacyDec{math.LegacyNewDec(2), math.LegacyNewDec(4)}, false},
{[]math.LegacyDec{math.LegacyNewDec(3), math.LegacyNewDec(18)}, []math.LegacyDec{math.LegacyNewDec(1), math.LegacyNewDec(6)}, false},
}
for tcIndex, tc := range tests {
s.Require().Equal(tc.eq, math.LegacyDecsEqual(tc.d1s, tc.d2s), "equality of decional arrays is incorrect, tc %d", tcIndex)
s.Require().Equal(tc.eq, math.LegacyDecsEqual(tc.d2s, tc.d1s), "equality of decional arrays is incorrect (converse), tc %d", tcIndex)
}
}
func (s *decimalTestSuite) TestArithmetic() {
tests := []struct {
d1, d2 math.LegacyDec
expMul, expMulTruncate math.LegacyDec
expQuo, expQuoRoundUp, expQuoTruncate math.LegacyDec
expAdd, expSub math.LegacyDec
}{
// d1 d2 MUL MulTruncate QUO QUORoundUp QUOTrunctate ADD SUB
{math.LegacyNewDec(0), math.LegacyNewDec(0), math.LegacyNewDec(0), math.LegacyNewDec(0), math.LegacyNewDec(0), math.LegacyNewDec(0), math.LegacyNewDec(0), math.LegacyNewDec(0), math.LegacyNewDec(0)},
{math.LegacyNewDec(1), math.LegacyNewDec(0), math.LegacyNewDec(0), math.LegacyNewDec(0), math.LegacyNewDec(0), math.LegacyNewDec(0), math.LegacyNewDec(0), math.LegacyNewDec(1), math.LegacyNewDec(1)},
{math.LegacyNewDec(0), math.LegacyNewDec(1), math.LegacyNewDec(0), math.LegacyNewDec(0), math.LegacyNewDec(0), math.LegacyNewDec(0), math.LegacyNewDec(0), math.LegacyNewDec(1), math.LegacyNewDec(-1)},
{math.LegacyNewDec(0), math.LegacyNewDec(-1), math.LegacyNewDec(0), math.LegacyNewDec(0), math.LegacyNewDec(0), math.LegacyNewDec(0), math.LegacyNewDec(0), math.LegacyNewDec(-1), math.LegacyNewDec(1)},
{math.LegacyNewDec(-1), math.LegacyNewDec(0), math.LegacyNewDec(0), math.LegacyNewDec(0), math.LegacyNewDec(0), math.LegacyNewDec(0), math.LegacyNewDec(0), math.LegacyNewDec(-1), math.LegacyNewDec(-1)},
{math.LegacyNewDec(1), math.LegacyNewDec(1), math.LegacyNewDec(1), math.LegacyNewDec(1), math.LegacyNewDec(1), math.LegacyNewDec(1), math.LegacyNewDec(1), math.LegacyNewDec(2), math.LegacyNewDec(0)},
{math.LegacyNewDec(-1), math.LegacyNewDec(-1), math.LegacyNewDec(1), math.LegacyNewDec(1), math.LegacyNewDec(1), math.LegacyNewDec(1), math.LegacyNewDec(1), math.LegacyNewDec(-2), math.LegacyNewDec(0)},
{math.LegacyNewDec(1), math.LegacyNewDec(-1), math.LegacyNewDec(-1), math.LegacyNewDec(-1), math.LegacyNewDec(-1), math.LegacyNewDec(-1), math.LegacyNewDec(-1), math.LegacyNewDec(0), math.LegacyNewDec(2)},
{math.LegacyNewDec(-1), math.LegacyNewDec(1), math.LegacyNewDec(-1), math.LegacyNewDec(-1), math.LegacyNewDec(-1), math.LegacyNewDec(-1), math.LegacyNewDec(-1), math.LegacyNewDec(0), math.LegacyNewDec(-2)},
{
math.LegacyNewDec(3), math.LegacyNewDec(7), math.LegacyNewDec(21), math.LegacyNewDec(21),
math.LegacyNewDecWithPrec(428571428571428571, 18), math.LegacyNewDecWithPrec(428571428571428572, 18), math.LegacyNewDecWithPrec(428571428571428571, 18),
math.LegacyNewDec(10), math.LegacyNewDec(-4),
},
{
math.LegacyNewDec(2), math.LegacyNewDec(4), math.LegacyNewDec(8), math.LegacyNewDec(8), math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDecWithPrec(5, 1),
math.LegacyNewDec(6), math.LegacyNewDec(-2),
},
{math.LegacyNewDec(100), math.LegacyNewDec(100), math.LegacyNewDec(10000), math.LegacyNewDec(10000), math.LegacyNewDec(1), math.LegacyNewDec(1), math.LegacyNewDec(1), math.LegacyNewDec(200), math.LegacyNewDec(0)},
{
math.LegacyNewDecWithPrec(15, 1), math.LegacyNewDecWithPrec(15, 1), math.LegacyNewDecWithPrec(225, 2), math.LegacyNewDecWithPrec(225, 2),
math.LegacyNewDec(1), math.LegacyNewDec(1), math.LegacyNewDec(1), math.LegacyNewDec(3), math.LegacyNewDec(0),
},
{
math.LegacyNewDecWithPrec(3333, 4), math.LegacyNewDecWithPrec(333, 4), math.LegacyNewDecWithPrec(1109889, 8), math.LegacyNewDecWithPrec(1109889, 8),
math.LegacyMustNewDecFromStr("10.009009009009009009"), math.LegacyMustNewDecFromStr("10.009009009009009010"), math.LegacyMustNewDecFromStr("10.009009009009009009"),
math.LegacyNewDecWithPrec(3666, 4), math.LegacyNewDecWithPrec(3, 1),
},
}
for tcIndex, tc := range tests {
tc := tc
resAdd := tc.d1.Add(tc.d2)
resSub := tc.d1.Sub(tc.d2)
resMul := tc.d1.Mul(tc.d2)
resMulTruncate := tc.d1.MulTruncate(tc.d2)
s.Require().True(tc.expAdd.Equal(resAdd), "exp %v, res %v, tc %d", tc.expAdd, resAdd, tcIndex)
s.Require().True(tc.expSub.Equal(resSub), "exp %v, res %v, tc %d", tc.expSub, resSub, tcIndex)
s.Require().True(tc.expMul.Equal(resMul), "exp %v, res %v, tc %d", tc.expMul, resMul, tcIndex)
s.Require().True(tc.expMulTruncate.Equal(resMulTruncate), "exp %v, res %v, tc %d", tc.expMulTruncate, resMulTruncate, tcIndex)
if tc.d2.IsZero() { // panic for divide by zero
s.Require().Panics(func() { tc.d1.Quo(tc.d2) })
} else {
resQuo := tc.d1.Quo(tc.d2)
s.Require().True(tc.expQuo.Equal(resQuo), "exp %v, res %v, tc %d", tc.expQuo.String(), resQuo.String(), tcIndex)
resQuoRoundUp := tc.d1.QuoRoundUp(tc.d2)
s.Require().True(tc.expQuoRoundUp.Equal(resQuoRoundUp), "exp %v, res %v, tc %d",
tc.expQuoRoundUp.String(), resQuoRoundUp.String(), tcIndex)
resQuoTruncate := tc.d1.QuoTruncate(tc.d2)
s.Require().True(tc.expQuoTruncate.Equal(resQuoTruncate), "exp %v, res %v, tc %d",
tc.expQuoTruncate.String(), resQuoTruncate.String(), tcIndex)
}
}
}
func (s *decimalTestSuite) TestBankerRoundChop() {
tests := []struct {
d1 math.LegacyDec
exp int64
}{
{s.mustNewDecFromStr("0.25"), 0},
{s.mustNewDecFromStr("0"), 0},
{s.mustNewDecFromStr("1"), 1},
{s.mustNewDecFromStr("0.75"), 1},
{s.mustNewDecFromStr("0.5"), 0},
{s.mustNewDecFromStr("7.5"), 8},
{s.mustNewDecFromStr("1.5"), 2},
{s.mustNewDecFromStr("2.5"), 2},
{s.mustNewDecFromStr("0.545"), 1}, // 0.545-> 1 even though 5 is first decimal and 1 not even
{s.mustNewDecFromStr("1.545"), 2},
}
for tcIndex, tc := range tests {
resNeg := tc.d1.Neg().RoundInt64()
s.Require().Equal(-1*tc.exp, resNeg, "negative tc %d", tcIndex)
resPos := tc.d1.RoundInt64()
s.Require().Equal(tc.exp, resPos, "positive tc %d", tcIndex)
}
}
func (s *decimalTestSuite) TestTruncate() {
tests := []struct {
d1 math.LegacyDec
exp int64
}{
{s.mustNewDecFromStr("0"), 0},
{s.mustNewDecFromStr("0.25"), 0},
{s.mustNewDecFromStr("0.75"), 0},
{s.mustNewDecFromStr("1"), 1},
{s.mustNewDecFromStr("1.5"), 1},
{s.mustNewDecFromStr("7.5"), 7},
{s.mustNewDecFromStr("7.6"), 7},
{s.mustNewDecFromStr("7.4"), 7},
{s.mustNewDecFromStr("100.1"), 100},
{s.mustNewDecFromStr("1000.1"), 1000},
}
for tcIndex, tc := range tests {
resNeg := tc.d1.Neg().TruncateInt64()
s.Require().Equal(-1*tc.exp, resNeg, "negative tc %d", tcIndex)
resPos := tc.d1.TruncateInt64()
s.Require().Equal(tc.exp, resPos, "positive tc %d", tcIndex)
}
}
func (s *decimalTestSuite) TestStringOverflow() {
// two random 64 bit primes
dec1, err := math.LegacyNewDecFromStr("51643150036226787134389711697696177267")
s.Require().NoError(err)
dec2, err := math.LegacyNewDecFromStr("-31798496660535729618459429845579852627")
s.Require().NoError(err)
dec3 := dec1.Add(dec2)
s.Require().Equal(
"19844653375691057515930281852116324640.000000000000000000",
dec3.String(),
)
}
func (s *decimalTestSuite) TestDecMulInt() {
tests := []struct {
sdkDec math.LegacyDec
sdkInt math.Int
want math.LegacyDec
}{
{math.LegacyNewDec(10), math.NewInt(2), math.LegacyNewDec(20)},
{math.LegacyNewDec(1000000), math.NewInt(100), math.LegacyNewDec(100000000)},
{math.LegacyNewDecWithPrec(1, 1), math.NewInt(10), math.LegacyNewDec(1)},
{math.LegacyNewDecWithPrec(1, 5), math.NewInt(20), math.LegacyNewDecWithPrec(2, 4)},
}
for i, tc := range tests {
got := tc.sdkDec.MulInt(tc.sdkInt)
s.Require().Equal(tc.want, got, "Incorrect result on test case %d", i)
}
}
func (s *decimalTestSuite) TestDecCeil() {
testCases := []struct {
input math.LegacyDec
expected math.LegacyDec
}{
{math.LegacyNewDecWithPrec(1000000000000000, math.LegacyPrecision), math.LegacyNewDec(1)}, // 0.001 => 1.0
{math.LegacyNewDecWithPrec(-1000000000000000, math.LegacyPrecision), math.LegacyZeroDec()}, // -0.001 => 0.0
{math.LegacyZeroDec(), math.LegacyZeroDec()}, // 0.0 => 0.0
{math.LegacyNewDecWithPrec(900000000000000000, math.LegacyPrecision), math.LegacyNewDec(1)}, // 0.9 => 1.0
{math.LegacyNewDecWithPrec(4001000000000000000, math.LegacyPrecision), math.LegacyNewDec(5)}, // 4.001 => 5.0
{math.LegacyNewDecWithPrec(-4001000000000000000, math.LegacyPrecision), math.LegacyNewDec(-4)}, // -4.001 => -4.0
{math.LegacyNewDecWithPrec(4700000000000000000, math.LegacyPrecision), math.LegacyNewDec(5)}, // 4.7 => 5.0
{math.LegacyNewDecWithPrec(-4700000000000000000, math.LegacyPrecision), math.LegacyNewDec(-4)}, // -4.7 => -4.0
}
for i, tc := range testCases {
res := tc.input.Ceil()
s.Require().Equal(tc.expected, res, "unexpected result for test case %d, input: %v", i, tc.input)
}
}
func (s *decimalTestSuite) TestPower() {
testCases := []struct {
input math.LegacyDec
power uint64
expected math.LegacyDec
}{
{math.LegacyNewDec(100), 0, math.LegacyOneDec()}, // 10 ^ (0) => 1.0
{math.LegacyOneDec(), 10, math.LegacyOneDec()}, // 1.0 ^ (10) => 1.0
{math.LegacyNewDecWithPrec(5, 1), 2, math.LegacyNewDecWithPrec(25, 2)}, // 0.5 ^ 2 => 0.25
{math.LegacyNewDecWithPrec(2, 1), 2, math.LegacyNewDecWithPrec(4, 2)}, // 0.2 ^ 2 => 0.04
{math.LegacyNewDecFromInt(math.NewInt(3)), 3, math.LegacyNewDecFromInt(math.NewInt(27))}, // 3 ^ 3 => 27
{math.LegacyNewDecFromInt(math.NewInt(-3)), 4, math.LegacyNewDecFromInt(math.NewInt(81))}, // -3 ^ 4 = 81
{math.LegacyNewDecWithPrec(1414213562373095049, 18), 2, math.LegacyNewDecFromInt(math.NewInt(2))}, // 1.414213562373095049 ^ 2 = 2
}
for i, tc := range testCases {
res := tc.input.Power(tc.power)
s.Require().True(tc.expected.Sub(res).Abs().LTE(math.LegacySmallestDec()), "unexpected result for test case %d, normal power, input: %v", i, tc.input)
mutableInput := tc.input
mutableInput.PowerMut(tc.power)
s.Require().True(tc.expected.Sub(mutableInput).Abs().LTE(math.LegacySmallestDec()),
"unexpected result for test case %d, input %v", i, tc.input)
s.Require().True(res.Equal(tc.input), "unexpected result for test case %d, mutable power, input: %v", i, tc.input)
}
}
func (s *decimalTestSuite) TestApproxRoot() {
testCases := []struct {
input math.LegacyDec
root uint64
expected math.LegacyDec
}{
{math.LegacyOneDec(), 10, math.LegacyOneDec()}, // 1.0 ^ (0.1) => 1.0
{math.LegacyNewDecWithPrec(25, 2), 2, math.LegacyNewDecWithPrec(5, 1)}, // 0.25 ^ (0.5) => 0.5
{math.LegacyNewDecWithPrec(4, 2), 2, math.LegacyNewDecWithPrec(2, 1)}, // 0.04 ^ (0.5) => 0.2
{math.LegacyNewDecFromInt(math.NewInt(27)), 3, math.LegacyNewDecFromInt(math.NewInt(3))}, // 27 ^ (1/3) => 3
{math.LegacyNewDecFromInt(math.NewInt(-81)), 4, math.LegacyNewDecFromInt(math.NewInt(-3))}, // -81 ^ (0.25) => -3
{math.LegacyNewDecFromInt(math.NewInt(2)), 2, math.LegacyNewDecWithPrec(1414213562373095049, 18)}, // 2 ^ (0.5) => 1.414213562373095049
{math.LegacyNewDecWithPrec(1005, 3), 31536000, math.LegacyMustNewDecFromStr("1.000000000158153904")}, // 1.005 ^ (1/31536000) ≈ 1.00000000016
{math.LegacySmallestDec(), 2, math.LegacyNewDecWithPrec(1, 9)}, // 1e-18 ^ (0.5) => 1e-9
{math.LegacySmallestDec(), 3, math.LegacyMustNewDecFromStr("0.000000999999999997")}, // 1e-18 ^ (1/3) => 1e-6
{math.LegacyNewDecWithPrec(1, 8), 3, math.LegacyMustNewDecFromStr("0.002154434690031900")}, // 1e-8 ^ (1/3) ≈ 0.00215443469
{math.LegacyMustNewDecFromStr("9000002314687921634000000000000000000021394871242000000000000000"), 2, math.LegacyMustNewDecFromStr("94868342004527103646332858502867.899477053226766107")},
}
// In the case of 1e-8 ^ (1/3), the result repeats every 5 iterations starting from iteration 24
// (i.e. 24, 29, 34, ... give the same result) and never converges enough. The maximum number of
// iterations (300) causes the result at iteration 300 to be returned, regardless of convergence.
for i, tc := range testCases {
res, err := tc.input.ApproxRoot(tc.root)
s.Require().NoError(err)
s.Require().True(tc.expected.Sub(res).Abs().LTE(math.LegacySmallestDec()), "unexpected result for test case %d, input: %v", i, tc.input)
}
}
func (s *decimalTestSuite) TestApproxSqrt() {
testCases := []struct {
input math.LegacyDec
expected math.LegacyDec
}{
{math.LegacyOneDec(), math.LegacyOneDec()}, // 1.0 => 1.0
{math.LegacyNewDecWithPrec(25, 2), math.LegacyNewDecWithPrec(5, 1)}, // 0.25 => 0.5
{math.LegacyNewDecWithPrec(4, 2), math.LegacyNewDecWithPrec(2, 1)}, // 0.09 => 0.3
{math.LegacyNewDecFromInt(math.NewInt(9)), math.LegacyNewDecFromInt(math.NewInt(3))}, // 9 => 3
{math.LegacyNewDecFromInt(math.NewInt(-9)), math.LegacyNewDecFromInt(math.NewInt(-3))}, // -9 => -3
{math.LegacyNewDecFromInt(math.NewInt(2)), math.LegacyNewDecWithPrec(1414213562373095049, 18)}, // 2 => 1.414213562373095049
}
for i, tc := range testCases {
res, err := tc.input.ApproxSqrt()
s.Require().NoError(err)
s.Require().Equal(tc.expected, res, "unexpected result for test case %d, input: %v", i, tc.input)
}
}
func (s *decimalTestSuite) TestDecSortableBytes() {
tests := []struct {
d math.LegacyDec
want []byte
}{
{math.LegacyNewDec(0), []byte("000000000000000000.000000000000000000")},
{math.LegacyNewDec(1), []byte("000000000000000001.000000000000000000")},
{math.LegacyNewDec(10), []byte("000000000000000010.000000000000000000")},
{math.LegacyNewDec(12340), []byte("000000000000012340.000000000000000000")},
{math.LegacyNewDecWithPrec(12340, 4), []byte("000000000000000001.234000000000000000")},
{math.LegacyNewDecWithPrec(12340, 5), []byte("000000000000000000.123400000000000000")},
{math.LegacyNewDecWithPrec(12340, 8), []byte("000000000000000000.000123400000000000")},
{math.LegacyNewDecWithPrec(1009009009009009009, 17), []byte("000000000000000010.090090090090090090")},
{math.LegacyNewDecWithPrec(-1009009009009009009, 17), []byte("-000000000000000010.090090090090090090")},
{math.LegacyNewDec(1000000000000000000), []byte("max")},
{math.LegacyNewDec(-1000000000000000000), []byte("--")},
}
for tcIndex, tc := range tests {
s.Require().Equal(tc.want, math.LegacySortableDecBytes(tc.d), "bad String(), index: %v", tcIndex)
}
s.Require().Panics(func() { math.LegacySortableDecBytes(math.LegacyNewDec(1000000000000000001)) })
s.Require().Panics(func() { math.LegacySortableDecBytes(math.LegacyNewDec(-1000000000000000001)) })
}
func (s *decimalTestSuite) TestDecEncoding() {
largestBigInt, ok := new(big.Int).SetString("33499189745056880149688856635597007162669032647290798121690100488888732861290034376435130433535", 10)
s.Require().True(ok)
smallestBigInt, ok := new(big.Int).SetString("-33499189745056880149688856635597007162669032647290798121690100488888732861290034376435130433535", 10)
s.Require().True(ok)
const maxDecBitLen = 315
maxInt, ok := new(big.Int).SetString(strings.Repeat("1", maxDecBitLen), 2)
s.Require().True(ok)
testCases := []struct {
input math.LegacyDec
rawBz string
jsonStr string
yamlStr string
}{
{
math.LegacyNewDec(0), "30",
"\"0.000000000000000000\"",
"\"0.000000000000000000\"\n",
},
{
math.LegacyNewDecWithPrec(4, 2),
"3430303030303030303030303030303030",
"\"0.040000000000000000\"",
"\"0.040000000000000000\"\n",
},
{
math.LegacyNewDecWithPrec(-4, 2),
"2D3430303030303030303030303030303030",
"\"-0.040000000000000000\"",
"\"-0.040000000000000000\"\n",
},
{
math.LegacyNewDecWithPrec(1414213562373095049, 18),
"31343134323133353632333733303935303439",
"\"1.414213562373095049\"",
"\"1.414213562373095049\"\n",
},
{
math.LegacyNewDecWithPrec(-1414213562373095049, 18),
"2D31343134323133353632333733303935303439",
"\"-1.414213562373095049\"",
"\"-1.414213562373095049\"\n",
},
{
math.LegacyNewDecFromBigIntWithPrec(largestBigInt, 18),
"3333343939313839373435303536383830313439363838383536363335353937303037313632363639303332363437323930373938313231363930313030343838383838373332383631323930303334333736343335313330343333353335",
"\"33499189745056880149688856635597007162669032647290798121690100488888732861290.034376435130433535\"",
"\"33499189745056880149688856635597007162669032647290798121690100488888732861290.034376435130433535\"\n",
},
{
math.LegacyNewDecFromBigIntWithPrec(smallestBigInt, 18),
"2D3333343939313839373435303536383830313439363838383536363335353937303037313632363639303332363437323930373938313231363930313030343838383838373332383631323930303334333736343335313330343333353335",
"\"-33499189745056880149688856635597007162669032647290798121690100488888732861290.034376435130433535\"",
"\"-33499189745056880149688856635597007162669032647290798121690100488888732861290.034376435130433535\"\n",
},
{
math.LegacyNewDecFromBigIntWithPrec(maxInt, 18),
"3636373439353934383732353238343430303734383434343238333137373938353033353831333334353136333233363435333939303630383435303530323434343434333636343330363435303137313838323137353635323136373637",
"\"66749594872528440074844428317798503581334516323645399060845050244444366430645.017188217565216767\"",
"\"66749594872528440074844428317798503581334516323645399060845050244444366430645.017188217565216767\"\n",
},
}
for _, tc := range testCases {
bz, err := tc.input.Marshal()
s.Require().NoError(err)
s.Require().Equal(tc.rawBz, fmt.Sprintf("%X", bz))
var other math.LegacyDec
s.Require().NoError((&other).Unmarshal(bz))
s.Require().True(tc.input.Equal(other))
bz, err = json.Marshal(tc.input)
s.Require().NoError(err)
s.Require().Equal(tc.jsonStr, string(bz))
s.Require().NoError(json.Unmarshal(bz, &other))
s.Require().True(tc.input.Equal(other))
bz, err = yaml.Marshal(tc.input)
s.Require().NoError(err)
s.Require().Equal(tc.yamlStr, string(bz))
}
}
// Showcase that different orders of operations causes different results.
func (s *decimalTestSuite) TestOperationOrders() {
n1 := math.LegacyNewDec(10)
n2 := math.LegacyNewDec(1000000010)
s.Require().Equal(n1.Mul(n2).Quo(n2), math.LegacyNewDec(10))
s.Require().NotEqual(n1.Mul(n2).Quo(n2), n1.Quo(n2).Mul(n2))
}
func BenchmarkMarshalTo(b *testing.B) {
b.ReportAllocs()
bis := []struct {
in math.LegacyDec
want []byte
}{
{
math.LegacyNewDec(1e8), []byte{
0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
},
},
{math.LegacyNewDec(0), []byte{0x30}},
}
data := make([]byte, 100)
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
for _, bi := range bis {
if n, err := bi.in.MarshalTo(data); err != nil {
b.Fatal(err)
} else {
if !bytes.Equal(data[:n], bi.want) {
b.Fatalf("Mismatch\nGot: % x\nWant: % x\n", data[:n], bi.want)
}
}
}
}
}

View File

@ -2,10 +2,14 @@ module cosmossdk.io/math
go 1.18
require github.com/stretchr/testify v1.8.0
require (
github.com/stretchr/testify v1.8.0
sigs.k8s.io/yaml v1.3.0
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

View File

@ -10,6 +10,10 @@ github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PK
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=

View File

@ -1,621 +0,0 @@
package types_test
import (
"bytes"
"encoding/json"
"fmt"
"math/big"
"strings"
"testing"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"sigs.k8s.io/yaml"
sdk "github.com/cosmos/cosmos-sdk/types"
)
type decimalTestSuite struct {
suite.Suite
}
func TestDecimalTestSuite(t *testing.T) {
suite.Run(t, new(decimalTestSuite))
}
func TestDecApproxEq(t *testing.T) {
// d1 = 0.55, d2 = 0.6, tol = 0.1
d1 := sdk.NewDecWithPrec(55, 2)
d2 := sdk.NewDecWithPrec(6, 1)
tol := sdk.NewDecWithPrec(1, 1)
require.True(sdk.DecApproxEq(t, d1, d2, tol))
// d1 = 0.55, d2 = 0.6, tol = 1E-5
d1 = sdk.NewDecWithPrec(55, 2)
d2 = sdk.NewDecWithPrec(6, 1)
tol = sdk.NewDecWithPrec(1, 5)
require.False(sdk.DecApproxEq(t, d1, d2, tol))
// d1 = 0.6, d2 = 0.61, tol = 0.01
d1 = sdk.NewDecWithPrec(6, 1)
d2 = sdk.NewDecWithPrec(61, 2)
tol = sdk.NewDecWithPrec(1, 2)
require.True(sdk.DecApproxEq(t, d1, d2, tol))
}
// create a decimal from a decimal string (ex. "1234.5678")
func (s *decimalTestSuite) mustNewDecFromStr(str string) (d sdk.Dec) {
d, err := sdk.NewDecFromStr(str)
s.Require().NoError(err)
return d
}
func (s *decimalTestSuite) TestNewDecFromStr() {
largeBigInt, ok := new(big.Int).SetString("3144605511029693144278234343371835", 10)
s.Require().True(ok)
largerBigInt, ok := new(big.Int).SetString("8888888888888888888888888888888888888888888888888888888888888888888844444440", 10)
s.Require().True(ok)
largestBigInt, ok := new(big.Int).SetString("33499189745056880149688856635597007162669032647290798121690100488888732861290034376435130433535", 10)
s.Require().True(ok)
tests := []struct {
decimalStr string
expErr bool
exp sdk.Dec
}{
{"", true, sdk.Dec{}},
{"0.-75", true, sdk.Dec{}},
{"0", false, sdk.NewDec(0)},
{"1", false, sdk.NewDec(1)},
{"1.1", false, sdk.NewDecWithPrec(11, 1)},
{"0.75", false, sdk.NewDecWithPrec(75, 2)},
{"0.8", false, sdk.NewDecWithPrec(8, 1)},
{"0.11111", false, sdk.NewDecWithPrec(11111, 5)},
{"314460551102969.3144278234343371835", true, sdk.NewDec(3141203149163817869)},
{
"314460551102969314427823434337.1835718092488231350",
true, sdk.NewDecFromBigIntWithPrec(largeBigInt, 4),
},
{
"314460551102969314427823434337.1835",
false, sdk.NewDecFromBigIntWithPrec(largeBigInt, 4),
},
{".", true, sdk.Dec{}},
{".0", true, sdk.NewDec(0)},
{"1.", true, sdk.NewDec(1)},
{"foobar", true, sdk.Dec{}},
{"0.foobar", true, sdk.Dec{}},
{"0.foobar.", true, sdk.Dec{}},
{"8888888888888888888888888888888888888888888888888888888888888888888844444440", false, sdk.NewDecFromBigInt(largerBigInt)},
{"33499189745056880149688856635597007162669032647290798121690100488888732861290.034376435130433535", false, sdk.NewDecFromBigIntWithPrec(largestBigInt, 18)},
{"133499189745056880149688856635597007162669032647290798121690100488888732861291", true, sdk.Dec{}},
}
for tcIndex, tc := range tests {
res, err := sdk.NewDecFromStr(tc.decimalStr)
if tc.expErr {
s.Require().NotNil(err, "error expected, decimalStr %v, tc %v", tc.decimalStr, tcIndex)
} else {
s.Require().Nil(err, "unexpected error, decimalStr %v, tc %v", tc.decimalStr, tcIndex)
s.Require().True(res.Equal(tc.exp), "equality was incorrect, res %v, exp %v, tc %v", res, tc.exp, tcIndex)
}
// negative tc
res, err = sdk.NewDecFromStr("-" + tc.decimalStr)
if tc.expErr {
s.Require().NotNil(err, "error expected, decimalStr %v, tc %v", tc.decimalStr, tcIndex)
} else {
s.Require().Nil(err, "unexpected error, decimalStr %v, tc %v", tc.decimalStr, tcIndex)
exp := tc.exp.Mul(sdk.NewDec(-1))
s.Require().True(res.Equal(exp), "equality was incorrect, res %v, exp %v, tc %v", res, exp, tcIndex)
}
}
}
func (s *decimalTestSuite) TestDecString() {
tests := []struct {
d sdk.Dec
want string
}{
{sdk.NewDec(0), "0.000000000000000000"},
{sdk.NewDec(1), "1.000000000000000000"},
{sdk.NewDec(10), "10.000000000000000000"},
{sdk.NewDec(12340), "12340.000000000000000000"},
{sdk.NewDecWithPrec(12340, 4), "1.234000000000000000"},
{sdk.NewDecWithPrec(12340, 5), "0.123400000000000000"},
{sdk.NewDecWithPrec(12340, 8), "0.000123400000000000"},
{sdk.NewDecWithPrec(1009009009009009009, 17), "10.090090090090090090"},
}
for tcIndex, tc := range tests {
s.Require().Equal(tc.want, tc.d.String(), "bad String(), index: %v", tcIndex)
}
}
func (s *decimalTestSuite) TestDecFloat64() {
tests := []struct {
d sdk.Dec
want float64
}{
{sdk.NewDec(0), 0.000000000000000000},
{sdk.NewDec(1), 1.000000000000000000},
{sdk.NewDec(10), 10.000000000000000000},
{sdk.NewDec(12340), 12340.000000000000000000},
{sdk.NewDecWithPrec(12340, 4), 1.234000000000000000},
{sdk.NewDecWithPrec(12340, 5), 0.123400000000000000},
{sdk.NewDecWithPrec(12340, 8), 0.000123400000000000},
{sdk.NewDecWithPrec(1009009009009009009, 17), 10.090090090090090090},
}
for tcIndex, tc := range tests {
value, err := tc.d.Float64()
s.Require().Nil(err, "error getting Float64(), index: %v", tcIndex)
s.Require().Equal(tc.want, value, "bad Float64(), index: %v", tcIndex)
s.Require().Equal(tc.want, tc.d.MustFloat64(), "bad MustFloat64(), index: %v", tcIndex)
}
}
func (s *decimalTestSuite) TestEqualities() {
tests := []struct {
d1, d2 sdk.Dec
gt, lt, eq bool
}{
{sdk.NewDec(0), sdk.NewDec(0), false, false, true},
{sdk.NewDecWithPrec(0, 2), sdk.NewDecWithPrec(0, 4), false, false, true},
{sdk.NewDecWithPrec(100, 0), sdk.NewDecWithPrec(100, 0), false, false, true},
{sdk.NewDecWithPrec(-100, 0), sdk.NewDecWithPrec(-100, 0), false, false, true},
{sdk.NewDecWithPrec(-1, 1), sdk.NewDecWithPrec(-1, 1), false, false, true},
{sdk.NewDecWithPrec(3333, 3), sdk.NewDecWithPrec(3333, 3), false, false, true},
{sdk.NewDecWithPrec(0, 0), sdk.NewDecWithPrec(3333, 3), false, true, false},
{sdk.NewDecWithPrec(0, 0), sdk.NewDecWithPrec(100, 0), false, true, false},
{sdk.NewDecWithPrec(-1, 0), sdk.NewDecWithPrec(3333, 3), false, true, false},
{sdk.NewDecWithPrec(-1, 0), sdk.NewDecWithPrec(100, 0), false, true, false},
{sdk.NewDecWithPrec(1111, 3), sdk.NewDecWithPrec(100, 0), false, true, false},
{sdk.NewDecWithPrec(1111, 3), sdk.NewDecWithPrec(3333, 3), false, true, false},
{sdk.NewDecWithPrec(-3333, 3), sdk.NewDecWithPrec(-1111, 3), false, true, false},
{sdk.NewDecWithPrec(3333, 3), sdk.NewDecWithPrec(0, 0), true, false, false},
{sdk.NewDecWithPrec(100, 0), sdk.NewDecWithPrec(0, 0), true, false, false},
{sdk.NewDecWithPrec(3333, 3), sdk.NewDecWithPrec(-1, 0), true, false, false},
{sdk.NewDecWithPrec(100, 0), sdk.NewDecWithPrec(-1, 0), true, false, false},
{sdk.NewDecWithPrec(100, 0), sdk.NewDecWithPrec(1111, 3), true, false, false},
{sdk.NewDecWithPrec(3333, 3), sdk.NewDecWithPrec(1111, 3), true, false, false},
{sdk.NewDecWithPrec(-1111, 3), sdk.NewDecWithPrec(-3333, 3), true, false, false},
}
for tcIndex, tc := range tests {
s.Require().Equal(tc.gt, tc.d1.GT(tc.d2), "GT result is incorrect, tc %d", tcIndex)
s.Require().Equal(tc.lt, tc.d1.LT(tc.d2), "LT result is incorrect, tc %d", tcIndex)
s.Require().Equal(tc.eq, tc.d1.Equal(tc.d2), "equality result is incorrect, tc %d", tcIndex)
}
}
func (s *decimalTestSuite) TestDecsEqual() {
tests := []struct {
d1s, d2s []sdk.Dec
eq bool
}{
{[]sdk.Dec{sdk.NewDec(0)}, []sdk.Dec{sdk.NewDec(0)}, true},
{[]sdk.Dec{sdk.NewDec(0)}, []sdk.Dec{sdk.NewDec(1)}, false},
{[]sdk.Dec{sdk.NewDec(0)}, []sdk.Dec{}, false},
{[]sdk.Dec{sdk.NewDec(0), sdk.NewDec(1)}, []sdk.Dec{sdk.NewDec(0), sdk.NewDec(1)}, true},
{[]sdk.Dec{sdk.NewDec(1), sdk.NewDec(0)}, []sdk.Dec{sdk.NewDec(1), sdk.NewDec(0)}, true},
{[]sdk.Dec{sdk.NewDec(1), sdk.NewDec(0)}, []sdk.Dec{sdk.NewDec(0), sdk.NewDec(1)}, false},
{[]sdk.Dec{sdk.NewDec(1), sdk.NewDec(0)}, []sdk.Dec{sdk.NewDec(1)}, false},
{[]sdk.Dec{sdk.NewDec(1), sdk.NewDec(2)}, []sdk.Dec{sdk.NewDec(2), sdk.NewDec(4)}, false},
{[]sdk.Dec{sdk.NewDec(3), sdk.NewDec(18)}, []sdk.Dec{sdk.NewDec(1), sdk.NewDec(6)}, false},
}
for tcIndex, tc := range tests {
s.Require().Equal(tc.eq, sdk.DecsEqual(tc.d1s, tc.d2s), "equality of decional arrays is incorrect, tc %d", tcIndex)
s.Require().Equal(tc.eq, sdk.DecsEqual(tc.d2s, tc.d1s), "equality of decional arrays is incorrect (converse), tc %d", tcIndex)
}
}
func (s *decimalTestSuite) TestArithmetic() {
tests := []struct {
d1, d2 sdk.Dec
expMul, expMulTruncate sdk.Dec
expQuo, expQuoRoundUp, expQuoTruncate sdk.Dec
expAdd, expSub sdk.Dec
}{
// d1 d2 MUL MulTruncate QUO QUORoundUp QUOTrunctate ADD SUB
{sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(0)},
{sdk.NewDec(1), sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(1), sdk.NewDec(1)},
{sdk.NewDec(0), sdk.NewDec(1), sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(1), sdk.NewDec(-1)},
{sdk.NewDec(0), sdk.NewDec(-1), sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(-1), sdk.NewDec(1)},
{sdk.NewDec(-1), sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(-1), sdk.NewDec(-1)},
{sdk.NewDec(1), sdk.NewDec(1), sdk.NewDec(1), sdk.NewDec(1), sdk.NewDec(1), sdk.NewDec(1), sdk.NewDec(1), sdk.NewDec(2), sdk.NewDec(0)},
{sdk.NewDec(-1), sdk.NewDec(-1), sdk.NewDec(1), sdk.NewDec(1), sdk.NewDec(1), sdk.NewDec(1), sdk.NewDec(1), sdk.NewDec(-2), sdk.NewDec(0)},
{sdk.NewDec(1), sdk.NewDec(-1), sdk.NewDec(-1), sdk.NewDec(-1), sdk.NewDec(-1), sdk.NewDec(-1), sdk.NewDec(-1), sdk.NewDec(0), sdk.NewDec(2)},
{sdk.NewDec(-1), sdk.NewDec(1), sdk.NewDec(-1), sdk.NewDec(-1), sdk.NewDec(-1), sdk.NewDec(-1), sdk.NewDec(-1), sdk.NewDec(0), sdk.NewDec(-2)},
{
sdk.NewDec(3), sdk.NewDec(7), sdk.NewDec(21), sdk.NewDec(21),
sdk.NewDecWithPrec(428571428571428571, 18), sdk.NewDecWithPrec(428571428571428572, 18), sdk.NewDecWithPrec(428571428571428571, 18),
sdk.NewDec(10), sdk.NewDec(-4),
},
{
sdk.NewDec(2), sdk.NewDec(4), sdk.NewDec(8), sdk.NewDec(8), sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1),
sdk.NewDec(6), sdk.NewDec(-2),
},
{sdk.NewDec(100), sdk.NewDec(100), sdk.NewDec(10000), sdk.NewDec(10000), sdk.NewDec(1), sdk.NewDec(1), sdk.NewDec(1), sdk.NewDec(200), sdk.NewDec(0)},
{
sdk.NewDecWithPrec(15, 1), sdk.NewDecWithPrec(15, 1), sdk.NewDecWithPrec(225, 2), sdk.NewDecWithPrec(225, 2),
sdk.NewDec(1), sdk.NewDec(1), sdk.NewDec(1), sdk.NewDec(3), sdk.NewDec(0),
},
{
sdk.NewDecWithPrec(3333, 4), sdk.NewDecWithPrec(333, 4), sdk.NewDecWithPrec(1109889, 8), sdk.NewDecWithPrec(1109889, 8),
sdk.MustNewDecFromStr("10.009009009009009009"), sdk.MustNewDecFromStr("10.009009009009009010"), sdk.MustNewDecFromStr("10.009009009009009009"),
sdk.NewDecWithPrec(3666, 4), sdk.NewDecWithPrec(3, 1),
},
}
for tcIndex, tc := range tests {
tc := tc
resAdd := tc.d1.Add(tc.d2)
resSub := tc.d1.Sub(tc.d2)
resMul := tc.d1.Mul(tc.d2)
resMulTruncate := tc.d1.MulTruncate(tc.d2)
s.Require().True(tc.expAdd.Equal(resAdd), "exp %v, res %v, tc %d", tc.expAdd, resAdd, tcIndex)
s.Require().True(tc.expSub.Equal(resSub), "exp %v, res %v, tc %d", tc.expSub, resSub, tcIndex)
s.Require().True(tc.expMul.Equal(resMul), "exp %v, res %v, tc %d", tc.expMul, resMul, tcIndex)
s.Require().True(tc.expMulTruncate.Equal(resMulTruncate), "exp %v, res %v, tc %d", tc.expMulTruncate, resMulTruncate, tcIndex)
if tc.d2.IsZero() { // panic for divide by zero
s.Require().Panics(func() { tc.d1.Quo(tc.d2) })
} else {
resQuo := tc.d1.Quo(tc.d2)
s.Require().True(tc.expQuo.Equal(resQuo), "exp %v, res %v, tc %d", tc.expQuo.String(), resQuo.String(), tcIndex)
resQuoRoundUp := tc.d1.QuoRoundUp(tc.d2)
s.Require().True(tc.expQuoRoundUp.Equal(resQuoRoundUp), "exp %v, res %v, tc %d",
tc.expQuoRoundUp.String(), resQuoRoundUp.String(), tcIndex)
resQuoTruncate := tc.d1.QuoTruncate(tc.d2)
s.Require().True(tc.expQuoTruncate.Equal(resQuoTruncate), "exp %v, res %v, tc %d",
tc.expQuoTruncate.String(), resQuoTruncate.String(), tcIndex)
}
}
}
func (s *decimalTestSuite) TestBankerRoundChop() {
tests := []struct {
d1 sdk.Dec
exp int64
}{
{s.mustNewDecFromStr("0.25"), 0},
{s.mustNewDecFromStr("0"), 0},
{s.mustNewDecFromStr("1"), 1},
{s.mustNewDecFromStr("0.75"), 1},
{s.mustNewDecFromStr("0.5"), 0},
{s.mustNewDecFromStr("7.5"), 8},
{s.mustNewDecFromStr("1.5"), 2},
{s.mustNewDecFromStr("2.5"), 2},
{s.mustNewDecFromStr("0.545"), 1}, // 0.545-> 1 even though 5 is first decimal and 1 not even
{s.mustNewDecFromStr("1.545"), 2},
}
for tcIndex, tc := range tests {
resNeg := tc.d1.Neg().RoundInt64()
s.Require().Equal(-1*tc.exp, resNeg, "negative tc %d", tcIndex)
resPos := tc.d1.RoundInt64()
s.Require().Equal(tc.exp, resPos, "positive tc %d", tcIndex)
}
}
func (s *decimalTestSuite) TestTruncate() {
tests := []struct {
d1 sdk.Dec
exp int64
}{
{s.mustNewDecFromStr("0"), 0},
{s.mustNewDecFromStr("0.25"), 0},
{s.mustNewDecFromStr("0.75"), 0},
{s.mustNewDecFromStr("1"), 1},
{s.mustNewDecFromStr("1.5"), 1},
{s.mustNewDecFromStr("7.5"), 7},
{s.mustNewDecFromStr("7.6"), 7},
{s.mustNewDecFromStr("7.4"), 7},
{s.mustNewDecFromStr("100.1"), 100},
{s.mustNewDecFromStr("1000.1"), 1000},
}
for tcIndex, tc := range tests {
resNeg := tc.d1.Neg().TruncateInt64()
s.Require().Equal(-1*tc.exp, resNeg, "negative tc %d", tcIndex)
resPos := tc.d1.TruncateInt64()
s.Require().Equal(tc.exp, resPos, "positive tc %d", tcIndex)
}
}
func (s *decimalTestSuite) TestStringOverflow() {
// two random 64 bit primes
dec1, err := sdk.NewDecFromStr("51643150036226787134389711697696177267")
s.Require().NoError(err)
dec2, err := sdk.NewDecFromStr("-31798496660535729618459429845579852627")
s.Require().NoError(err)
dec3 := dec1.Add(dec2)
s.Require().Equal(
"19844653375691057515930281852116324640.000000000000000000",
dec3.String(),
)
}
func (s *decimalTestSuite) TestDecMulInt() {
tests := []struct {
sdkDec sdk.Dec
sdkInt sdk.Int
want sdk.Dec
}{
{sdk.NewDec(10), sdk.NewInt(2), sdk.NewDec(20)},
{sdk.NewDec(1000000), sdk.NewInt(100), sdk.NewDec(100000000)},
{sdk.NewDecWithPrec(1, 1), sdk.NewInt(10), sdk.NewDec(1)},
{sdk.NewDecWithPrec(1, 5), sdk.NewInt(20), sdk.NewDecWithPrec(2, 4)},
}
for i, tc := range tests {
got := tc.sdkDec.MulInt(tc.sdkInt)
s.Require().Equal(tc.want, got, "Incorrect result on test case %d", i)
}
}
func (s *decimalTestSuite) TestDecCeil() {
testCases := []struct {
input sdk.Dec
expected sdk.Dec
}{
{sdk.NewDecWithPrec(1000000000000000, sdk.Precision), sdk.NewDec(1)}, // 0.001 => 1.0
{sdk.NewDecWithPrec(-1000000000000000, sdk.Precision), sdk.ZeroDec()}, // -0.001 => 0.0
{sdk.ZeroDec(), sdk.ZeroDec()}, // 0.0 => 0.0
{sdk.NewDecWithPrec(900000000000000000, sdk.Precision), sdk.NewDec(1)}, // 0.9 => 1.0
{sdk.NewDecWithPrec(4001000000000000000, sdk.Precision), sdk.NewDec(5)}, // 4.001 => 5.0
{sdk.NewDecWithPrec(-4001000000000000000, sdk.Precision), sdk.NewDec(-4)}, // -4.001 => -4.0
{sdk.NewDecWithPrec(4700000000000000000, sdk.Precision), sdk.NewDec(5)}, // 4.7 => 5.0
{sdk.NewDecWithPrec(-4700000000000000000, sdk.Precision), sdk.NewDec(-4)}, // -4.7 => -4.0
}
for i, tc := range testCases {
res := tc.input.Ceil()
s.Require().Equal(tc.expected, res, "unexpected result for test case %d, input: %v", i, tc.input)
}
}
func (s *decimalTestSuite) TestPower() {
testCases := []struct {
input sdk.Dec
power uint64
expected sdk.Dec
}{
{sdk.NewDec(100), 0, sdk.OneDec()}, // 10 ^ (0) => 1.0
{sdk.OneDec(), 10, sdk.OneDec()}, // 1.0 ^ (10) => 1.0
{sdk.NewDecWithPrec(5, 1), 2, sdk.NewDecWithPrec(25, 2)}, // 0.5 ^ 2 => 0.25
{sdk.NewDecWithPrec(2, 1), 2, sdk.NewDecWithPrec(4, 2)}, // 0.2 ^ 2 => 0.04
{sdk.NewDecFromInt(sdk.NewInt(3)), 3, sdk.NewDecFromInt(sdk.NewInt(27))}, // 3 ^ 3 => 27
{sdk.NewDecFromInt(sdk.NewInt(-3)), 4, sdk.NewDecFromInt(sdk.NewInt(81))}, // -3 ^ 4 = 81
{sdk.NewDecWithPrec(1414213562373095049, 18), 2, sdk.NewDecFromInt(sdk.NewInt(2))}, // 1.414213562373095049 ^ 2 = 2
}
for i, tc := range testCases {
res := tc.input.Power(tc.power)
s.Require().True(tc.expected.Sub(res).Abs().LTE(sdk.SmallestDec()), "unexpected result for test case %d, normal power, input: %v", i, tc.input)
mutableInput := tc.input
mutableInput.PowerMut(tc.power)
s.Require().True(tc.expected.Sub(mutableInput).Abs().LTE(sdk.SmallestDec()),
"unexpected result for test case %d, input %v", i, tc.input)
s.Require().True(res.Equal(tc.input), "unexpected result for test case %d, mutable power, input: %v", i, tc.input)
}
}
func (s *decimalTestSuite) TestApproxRoot() {
testCases := []struct {
input sdk.Dec
root uint64
expected sdk.Dec
}{
{sdk.OneDec(), 10, sdk.OneDec()}, // 1.0 ^ (0.1) => 1.0
{sdk.NewDecWithPrec(25, 2), 2, sdk.NewDecWithPrec(5, 1)}, // 0.25 ^ (0.5) => 0.5
{sdk.NewDecWithPrec(4, 2), 2, sdk.NewDecWithPrec(2, 1)}, // 0.04 ^ (0.5) => 0.2
{sdk.NewDecFromInt(sdk.NewInt(27)), 3, sdk.NewDecFromInt(sdk.NewInt(3))}, // 27 ^ (1/3) => 3
{sdk.NewDecFromInt(sdk.NewInt(-81)), 4, sdk.NewDecFromInt(sdk.NewInt(-3))}, // -81 ^ (0.25) => -3
{sdk.NewDecFromInt(sdk.NewInt(2)), 2, sdk.NewDecWithPrec(1414213562373095049, 18)}, // 2 ^ (0.5) => 1.414213562373095049
{sdk.NewDecWithPrec(1005, 3), 31536000, sdk.MustNewDecFromStr("1.000000000158153904")}, // 1.005 ^ (1/31536000) ≈ 1.00000000016
{sdk.SmallestDec(), 2, sdk.NewDecWithPrec(1, 9)}, // 1e-18 ^ (0.5) => 1e-9
{sdk.SmallestDec(), 3, sdk.MustNewDecFromStr("0.000000999999999997")}, // 1e-18 ^ (1/3) => 1e-6
{sdk.NewDecWithPrec(1, 8), 3, sdk.MustNewDecFromStr("0.002154434690031900")}, // 1e-8 ^ (1/3) ≈ 0.00215443469
{sdk.MustNewDecFromStr("9000002314687921634000000000000000000021394871242000000000000000"), 2, sdk.MustNewDecFromStr("94868342004527103646332858502867.899477053226766107")},
}
// In the case of 1e-8 ^ (1/3), the result repeats every 5 iterations starting from iteration 24
// (i.e. 24, 29, 34, ... give the same result) and never converges enough. The maximum number of
// iterations (300) causes the result at iteration 300 to be returned, regardless of convergence.
for i, tc := range testCases {
res, err := tc.input.ApproxRoot(tc.root)
s.Require().NoError(err)
s.Require().True(tc.expected.Sub(res).Abs().LTE(sdk.SmallestDec()), "unexpected result for test case %d, input: %v", i, tc.input)
}
}
func (s *decimalTestSuite) TestApproxSqrt() {
testCases := []struct {
input sdk.Dec
expected sdk.Dec
}{
{sdk.OneDec(), sdk.OneDec()}, // 1.0 => 1.0
{sdk.NewDecWithPrec(25, 2), sdk.NewDecWithPrec(5, 1)}, // 0.25 => 0.5
{sdk.NewDecWithPrec(4, 2), sdk.NewDecWithPrec(2, 1)}, // 0.09 => 0.3
{sdk.NewDecFromInt(sdk.NewInt(9)), sdk.NewDecFromInt(sdk.NewInt(3))}, // 9 => 3
{sdk.NewDecFromInt(sdk.NewInt(-9)), sdk.NewDecFromInt(sdk.NewInt(-3))}, // -9 => -3
{sdk.NewDecFromInt(sdk.NewInt(2)), sdk.NewDecWithPrec(1414213562373095049, 18)}, // 2 => 1.414213562373095049
}
for i, tc := range testCases {
res, err := tc.input.ApproxSqrt()
s.Require().NoError(err)
s.Require().Equal(tc.expected, res, "unexpected result for test case %d, input: %v", i, tc.input)
}
}
func (s *decimalTestSuite) TestDecSortableBytes() {
tests := []struct {
d sdk.Dec
want []byte
}{
{sdk.NewDec(0), []byte("000000000000000000.000000000000000000")},
{sdk.NewDec(1), []byte("000000000000000001.000000000000000000")},
{sdk.NewDec(10), []byte("000000000000000010.000000000000000000")},
{sdk.NewDec(12340), []byte("000000000000012340.000000000000000000")},
{sdk.NewDecWithPrec(12340, 4), []byte("000000000000000001.234000000000000000")},
{sdk.NewDecWithPrec(12340, 5), []byte("000000000000000000.123400000000000000")},
{sdk.NewDecWithPrec(12340, 8), []byte("000000000000000000.000123400000000000")},
{sdk.NewDecWithPrec(1009009009009009009, 17), []byte("000000000000000010.090090090090090090")},
{sdk.NewDecWithPrec(-1009009009009009009, 17), []byte("-000000000000000010.090090090090090090")},
{sdk.NewDec(1000000000000000000), []byte("max")},
{sdk.NewDec(-1000000000000000000), []byte("--")},
}
for tcIndex, tc := range tests {
s.Require().Equal(tc.want, sdk.SortableDecBytes(tc.d), "bad String(), index: %v", tcIndex)
}
s.Require().Panics(func() { sdk.SortableDecBytes(sdk.NewDec(1000000000000000001)) })
s.Require().Panics(func() { sdk.SortableDecBytes(sdk.NewDec(-1000000000000000001)) })
}
func (s *decimalTestSuite) TestDecEncoding() {
largestBigInt, ok := new(big.Int).SetString("33499189745056880149688856635597007162669032647290798121690100488888732861290034376435130433535", 10)
s.Require().True(ok)
smallestBigInt, ok := new(big.Int).SetString("-33499189745056880149688856635597007162669032647290798121690100488888732861290034376435130433535", 10)
s.Require().True(ok)
const maxDecBitLen = 315
maxInt, ok := new(big.Int).SetString(strings.Repeat("1", maxDecBitLen), 2)
s.Require().True(ok)
testCases := []struct {
input sdk.Dec
rawBz string
jsonStr string
yamlStr string
}{
{
sdk.NewDec(0), "30",
"\"0.000000000000000000\"",
"\"0.000000000000000000\"\n",
},
{
sdk.NewDecWithPrec(4, 2),
"3430303030303030303030303030303030",
"\"0.040000000000000000\"",
"\"0.040000000000000000\"\n",
},
{
sdk.NewDecWithPrec(-4, 2),
"2D3430303030303030303030303030303030",
"\"-0.040000000000000000\"",
"\"-0.040000000000000000\"\n",
},
{
sdk.NewDecWithPrec(1414213562373095049, 18),
"31343134323133353632333733303935303439",
"\"1.414213562373095049\"",
"\"1.414213562373095049\"\n",
},
{
sdk.NewDecWithPrec(-1414213562373095049, 18),
"2D31343134323133353632333733303935303439",
"\"-1.414213562373095049\"",
"\"-1.414213562373095049\"\n",
},
{
sdk.NewDecFromBigIntWithPrec(largestBigInt, 18),
"3333343939313839373435303536383830313439363838383536363335353937303037313632363639303332363437323930373938313231363930313030343838383838373332383631323930303334333736343335313330343333353335",
"\"33499189745056880149688856635597007162669032647290798121690100488888732861290.034376435130433535\"",
"\"33499189745056880149688856635597007162669032647290798121690100488888732861290.034376435130433535\"\n",
},
{
sdk.NewDecFromBigIntWithPrec(smallestBigInt, 18),
"2D3333343939313839373435303536383830313439363838383536363335353937303037313632363639303332363437323930373938313231363930313030343838383838373332383631323930303334333736343335313330343333353335",
"\"-33499189745056880149688856635597007162669032647290798121690100488888732861290.034376435130433535\"",
"\"-33499189745056880149688856635597007162669032647290798121690100488888732861290.034376435130433535\"\n",
},
{
sdk.NewDecFromBigIntWithPrec(maxInt, 18),
"3636373439353934383732353238343430303734383434343238333137373938353033353831333334353136333233363435333939303630383435303530323434343434333636343330363435303137313838323137353635323136373637",
"\"66749594872528440074844428317798503581334516323645399060845050244444366430645.017188217565216767\"",
"\"66749594872528440074844428317798503581334516323645399060845050244444366430645.017188217565216767\"\n",
},
}
for _, tc := range testCases {
bz, err := tc.input.Marshal()
s.Require().NoError(err)
s.Require().Equal(tc.rawBz, fmt.Sprintf("%X", bz))
var other sdk.Dec
s.Require().NoError((&other).Unmarshal(bz))
s.Require().True(tc.input.Equal(other))
bz, err = json.Marshal(tc.input)
s.Require().NoError(err)
s.Require().Equal(tc.jsonStr, string(bz))
s.Require().NoError(json.Unmarshal(bz, &other))
s.Require().True(tc.input.Equal(other))
bz, err = yaml.Marshal(tc.input)
s.Require().NoError(err)
s.Require().Equal(tc.yamlStr, string(bz))
}
}
// Showcase that different orders of operations causes different results.
func (s *decimalTestSuite) TestOperationOrders() {
n1 := sdk.NewDec(10)
n2 := sdk.NewDec(1000000010)
s.Require().Equal(n1.Mul(n2).Quo(n2), sdk.NewDec(10))
s.Require().NotEqual(n1.Mul(n2).Quo(n2), n1.Quo(n2).Mul(n2))
}
func BenchmarkMarshalTo(b *testing.B) {
b.ReportAllocs()
bis := []struct {
in sdk.Dec
want []byte
}{
{
sdk.NewDec(1e8), []byte{
0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
},
},
{sdk.NewDec(0), []byte{0x30}},
}
data := make([]byte, 100)
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
for _, bi := range bis {
if n, err := bi.in.MarshalTo(data); err != nil {
b.Fatal(err)
} else {
if !bytes.Equal(data[:n], bi.want) {
b.Fatalf("Mismatch\nGot: % x\nWant: % x\n", data[:n], bi.want)
}
}
}
}
}

View File

@ -35,3 +35,40 @@ const (
func (ip IntProto) String() string {
return ip.Int.String()
}
type (
Dec = sdkmath.LegacyDec
)
const (
Precision = sdkmath.LegacyPrecision
DecimalPrecisionBits = sdkmath.LegacyDecimalPrecisionBits
)
var (
ZeroDec = sdkmath.LegacyZeroDec
OneDec = sdkmath.LegacyOneDec
SmallestDec = sdkmath.LegacySmallestDec
NewDec = sdkmath.LegacyNewDec
NewDecWithPrec = sdkmath.LegacyNewDecWithPrec
NewDecFromBigInt = sdkmath.LegacyNewDecFromBigInt
NewDecFromBigIntWithPrec = sdkmath.LegacyNewDecFromBigIntWithPrec
NewDecFromInt = sdkmath.LegacyNewDecFromInt
NewDecFromIntWithPrec = sdkmath.LegacyNewDecFromIntWithPrec
NewDecFromStr = sdkmath.LegacyNewDecFromStr
MustNewDecFromStr = sdkmath.LegacyMustNewDecFromStr
MaxSortableDec = sdkmath.LegacyMaxSortableDec
ValidSortableDec = sdkmath.LegacyValidSortableDec
SortableDecBytes = sdkmath.LegacySortableDecBytes
DecsEqual = sdkmath.LegacyDecsEqual
MinDec = sdkmath.LegacyMinDec
MaxDec = sdkmath.LegacyMaxDec
DecEq = sdkmath.LegacyDecEq
DecApproxEq = sdkmath.LegacyDecApproxEq
)
var _ CustomProtobufType = (*Dec)(nil)
func (dp DecProto) String() string {
return dp.Dec.String()
}