diff --git a/PENDING.md b/PENDING.md index 7fcaaa187a..de7cce4f5c 100644 --- a/PENDING.md +++ b/PENDING.md @@ -31,6 +31,7 @@ BREAKING CHANGES * [\#2222] [x/staking] `/stake` -> `/staking` module rename * \#3292 [x/distribution] Enable or disable withdraw addresses with a parameter in the param store * [staking] \#1402 Redelegation and unbonding-delegation structs changed to include multiple an array of entries + * [\#3315] Increase decimal precision to 18 * Tendermint * [\#3298](https://github.com/cosmos/cosmos-sdk/issues/3298) Upgrade to Tendermint 0.28.0 diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index 68a749a322..e7761df253 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -499,7 +499,7 @@ func TestBonding(t *testing.T) { // query delegations, unbondings and redelegations from validator and delegator delegatorDels = getDelegatorDelegations(t, port, addr) require.Len(t, delegatorDels, 1) - require.Equal(t, "30.0000000000", delegatorDels[0].GetShares().String()) + require.Equal(t, "30.000000000000000000", delegatorDels[0].GetShares().String()) redelegation := getRedelegations(t, port, addr, operAddrs[0], operAddrs[1]) require.Len(t, redelegation, 1) diff --git a/types/decimal.go b/types/decimal.go index 5e0f8d2670..8c9eff424c 100644 --- a/types/decimal.go +++ b/types/decimal.go @@ -17,11 +17,11 @@ type Dec struct { // number of decimal places const ( - Precision = 10 + Precision = 18 // bytes required to represent the above precision - // ceil(log2(9999999999)) - DecimalPrecisionBits = 34 + // Ceiling[Log2[999 999 999 999 999 999]] + DecimalPrecisionBits = 60 ) var ( @@ -142,12 +142,14 @@ func NewDecFromStr(str string) (d Dec, err Error) { strs := strings.Split(str, ".") lenDecs := 0 combinedStr := strs[0] - if len(strs) == 2 { + + if len(strs) == 2 { // has a decimal place lenDecs = len(strs[1]) if lenDecs == 0 || len(combinedStr) == 0 { return d, ErrUnknownRequest("bad decimal length") } combinedStr = combinedStr + strs[1] + } else if len(strs) > 2 { return d, ErrUnknownRequest("too many periods to be a decimal string") } @@ -162,7 +164,7 @@ func NewDecFromStr(str string) (d Dec, err Error) { zeros := fmt.Sprintf(`%0`+strconv.Itoa(zerosToAdd)+`s`, "") combinedStr = combinedStr + zeros - combined, ok := new(big.Int).SetString(combinedStr, 10) + combined, ok := new(big.Int).SetString(combinedStr, 10) // base 10 if !ok { return d, ErrUnknownRequest(fmt.Sprintf("bad string to integer conversion, combinedStr: %v", combinedStr)) } @@ -276,36 +278,48 @@ func (d Dec) String() string { if d.IsNegative() { d = d.Neg() } - bz, err := d.Int.MarshalText() + + bzInt, err := d.Int.MarshalText() if err != nil { return "" } - var bzWDec []byte - inputSize := len(bz) + inputSize := len(bzInt) + + var bzStr []byte + // TODO: Remove trailing zeros // case 1, purely decimal - if inputSize <= 10 { - bzWDec = make([]byte, 12) + if inputSize <= Precision { + + bzStr = make([]byte, Precision+2) + // 0. prefix - bzWDec[0] = byte('0') - bzWDec[1] = byte('.') + bzStr[0] = byte('0') + bzStr[1] = byte('.') + // set relevant digits to 0 - for i := 0; i < 10-inputSize; i++ { - bzWDec[i+2] = byte('0') + for i := 0; i < Precision-inputSize; i++ { + bzStr[i+2] = byte('0') } - // set last few digits - copy(bzWDec[2+(10-inputSize):], bz) + + // set final digits + copy(bzStr[2+(Precision-inputSize):], bzInt) + } else { + // inputSize + 1 to account for the decimal point that is being added - bzWDec = make([]byte, inputSize+1) - copy(bzWDec, bz[:inputSize-10]) - bzWDec[inputSize-10] = byte('.') - copy(bzWDec[inputSize-9:], bz[inputSize-10:]) + 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 } + if isNeg { - return "-" + string(bzWDec) + return "-" + string(bzStr) } - return string(bzWDec) + return string(bzStr) } // ____ diff --git a/types/decimal_test.go b/types/decimal_test.go index fa6442f3a5..c42cf98b5f 100644 --- a/types/decimal_test.go +++ b/types/decimal_test.go @@ -22,7 +22,7 @@ func mustNewDecFromStr(t *testing.T, str string) (d Dec) { func TestPrecisionMultiplier(t *testing.T) { res := precisionMultiplier(5) - exp := big.NewInt(100000) + exp := big.NewInt(10000000000000) require.Equal(t, 0, res.Cmp(exp), "equality was incorrect, res %v, exp %v", res, exp) } @@ -76,6 +76,25 @@ func TestNewDecFromStr(t *testing.T) { } } +func TestDecString(t *testing.T) { + tests := []struct { + d Dec + want string + }{ + {NewDec(0), "0.000000000000000000"}, + {NewDec(1), "1.000000000000000000"}, + {NewDec(10), "10.000000000000000000"}, + {NewDec(12340), "12340.000000000000000000"}, + {NewDecWithPrec(12340, 4), "1.234000000000000000"}, + {NewDecWithPrec(12340, 5), "0.123400000000000000"}, + {NewDecWithPrec(12340, 8), "0.000123400000000000"}, + {NewDecWithPrec(1009009009009009009, 17), "10.090090090090090090"}, + } + for tcIndex, tc := range tests { + assert.Equal(t, tc.want, tc.d.String(), "bad String(), index: %v", tcIndex) + } +} + func TestEqualities(t *testing.T) { tests := []struct { d1, d2 Dec @@ -140,7 +159,7 @@ func TestArithmetic(t *testing.T) { d1, d2 Dec expMul, expDiv, expAdd, expSub Dec }{ - // d1 d2 MUL DIV ADD SUB + // d1 d2 MUL DIV ADD SUB {NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(0)}, {NewDec(1), NewDec(0), NewDec(0), NewDec(0), NewDec(1), NewDec(1)}, {NewDec(0), NewDec(1), NewDec(0), NewDec(0), NewDec(1), NewDec(-1)}, @@ -152,14 +171,14 @@ func TestArithmetic(t *testing.T) { {NewDec(1), NewDec(-1), NewDec(-1), NewDec(-1), NewDec(0), NewDec(2)}, {NewDec(-1), NewDec(1), NewDec(-1), NewDec(-1), NewDec(0), NewDec(-2)}, - {NewDec(3), NewDec(7), NewDec(21), NewDecWithPrec(4285714286, 10), NewDec(10), NewDec(-4)}, + {NewDec(3), NewDec(7), NewDec(21), NewDecWithPrec(428571428571428571, 18), NewDec(10), NewDec(-4)}, {NewDec(2), NewDec(4), NewDec(8), NewDecWithPrec(5, 1), NewDec(6), NewDec(-2)}, {NewDec(100), NewDec(100), NewDec(10000), NewDec(1), NewDec(200), NewDec(0)}, {NewDecWithPrec(15, 1), NewDecWithPrec(15, 1), NewDecWithPrec(225, 2), NewDec(1), NewDec(3), NewDec(0)}, {NewDecWithPrec(3333, 4), NewDecWithPrec(333, 4), NewDecWithPrec(1109889, 8), - NewDecWithPrec(10009009009, 9), NewDecWithPrec(3666, 4), NewDecWithPrec(3, 1)}, + MustNewDecFromStr("10.009009009009009009"), NewDecWithPrec(3666, 4), NewDecWithPrec(3, 1)}, } for tcIndex, tc := range tests { @@ -245,14 +264,14 @@ func TestDecMarshalJSON(t *testing.T) { want string wantErr bool // if wantErr = false, will also attempt unmarshaling }{ - {"zero", decimal(0), "\"0.0000000000\"", false}, - {"one", decimal(1), "\"0.0000000001\"", false}, - {"ten", decimal(10), "\"0.0000000010\"", false}, - {"12340", decimal(12340), "\"0.0000012340\"", false}, - {"zeroInt", NewDec(0), "\"0.0000000000\"", false}, - {"oneInt", NewDec(1), "\"1.0000000000\"", false}, - {"tenInt", NewDec(10), "\"10.0000000000\"", false}, - {"12340Int", NewDec(12340), "\"12340.0000000000\"", false}, + {"zero", decimal(0), "\"0.000000000000000000\"", false}, + {"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}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -344,7 +363,7 @@ func TestStringOverflow(t *testing.T) { require.NoError(t, err) dec3 := dec1.Add(dec2) require.Equal(t, - "19844653375691057515930281852116324640.0000000000", + "19844653375691057515930281852116324640.000000000000000000", dec3.String(), ) }