feat(math/Dec): Add comparison and utility APIs MinDec, MaxDec, Neg(), Abs(), GT(), GTE(), LT(), LTE() (#24343)
This commit is contained in:
parent
11c41291a4
commit
41e3e9d004
@ -40,6 +40,7 @@ Ref: https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.j
|
||||
|
||||
### Features
|
||||
|
||||
* [#24343](https://github.com/cosmos/cosmos-sdk/pull/24343) feat(math/Dec): Add comparison and utility APIs MinDec, MaxDec, Neg(), Abs(), GT(), GTE(), LT(), LTE().
|
||||
* [#24229](https://github.com/cosmos/cosmos-sdk/pull/24229) Add `DecFromLegacyDec` migration function.
|
||||
|
||||
## [math/v1.5.1](https://github.com/cosmos/cosmos-sdk/releases/tag/math/v1.5.1) - 2025-03-28
|
||||
|
||||
56
math/dec.go
56
math/dec.go
@ -254,6 +254,26 @@ func (x Dec) Mul(y Dec) (Dec, error) {
|
||||
return z, nil
|
||||
}
|
||||
|
||||
// Neg returns a new Dec with value `-x` (negation of x) without mutating the argument x.
|
||||
// It returns an error if the negation operation fails.
|
||||
func (x Dec) Neg() (Dec, error) {
|
||||
var z Dec
|
||||
if _, err := dec128Context.Neg(&z.dec, &x.dec); err != nil {
|
||||
return z, ErrInvalidDec.Wrap(err.Error())
|
||||
}
|
||||
return z, nil
|
||||
}
|
||||
|
||||
// Abs returns a new Dec with the absolute value of x without mutating the argument x.
|
||||
// It returns an error if the absolute value operation fails.
|
||||
func (x Dec) Abs() (Dec, error) {
|
||||
var z Dec
|
||||
if _, err := dec128Context.Abs(&z.dec, &x.dec); err != nil {
|
||||
return z, ErrInvalidDec.Wrap(err.Error())
|
||||
}
|
||||
return z, nil
|
||||
}
|
||||
|
||||
// MulExact multiplies two Dec values x and y without rounding, using decimal128 precision.
|
||||
// It returns an error if rounding is necessary to fit the result within the 34-digit limit.
|
||||
//
|
||||
@ -365,6 +385,26 @@ func (x Dec) Cmp(y Dec) int {
|
||||
return x.dec.Cmp(&y.dec)
|
||||
}
|
||||
|
||||
// LT (less than) returns true if x is less than y, false otherwise
|
||||
func (x Dec) LT(y Dec) bool {
|
||||
return x.Cmp(y) == -1
|
||||
}
|
||||
|
||||
// LTE (less than or equal) returns true if x is less than or equal to y, false otherwise
|
||||
func (x Dec) LTE(y Dec) bool {
|
||||
return x.Cmp(y) != 1
|
||||
}
|
||||
|
||||
// GT (greater than) returns true if x is greater than y, false otherwise
|
||||
func (x Dec) GT(y Dec) bool {
|
||||
return x.Cmp(y) == 1
|
||||
}
|
||||
|
||||
// GTE (greater than or equal) returns true if x is greater than or equal to y, false otherwise
|
||||
func (x Dec) GTE(y Dec) bool {
|
||||
return x.Cmp(y) != -1
|
||||
}
|
||||
|
||||
// Equal checks if the decimal values of x and y are exactly equal.
|
||||
// It returns true if both decimals represent the same value, otherwise false.
|
||||
func (x Dec) Equal(y Dec) bool {
|
||||
@ -520,3 +560,19 @@ func (x *Dec) UnmarshalJSON(data []byte) error {
|
||||
*x = val
|
||||
return nil
|
||||
}
|
||||
|
||||
// MinDec returns the smaller of x and y
|
||||
func MinDec(x, y Dec) Dec {
|
||||
if x.LT(y) {
|
||||
return x
|
||||
}
|
||||
return y
|
||||
}
|
||||
|
||||
// MaxDec returns the larger of x and y
|
||||
func MaxDec(x, y Dec) Dec {
|
||||
if x.GT(y) {
|
||||
return x
|
||||
}
|
||||
return y
|
||||
}
|
||||
|
||||
482
math/dec_test.go
482
math/dec_test.go
@ -1453,3 +1453,485 @@ func TestMarshalUnmarshal(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLT(t *testing.T) {
|
||||
specs := map[string]struct {
|
||||
x Dec
|
||||
y Dec
|
||||
exp bool
|
||||
}{
|
||||
"0 == 0": {
|
||||
x: NewDecFromInt64(0),
|
||||
y: NewDecFromInt64(0),
|
||||
exp: false,
|
||||
},
|
||||
"0 < 123": {
|
||||
x: NewDecFromInt64(0),
|
||||
y: NewDecFromInt64(123),
|
||||
exp: true,
|
||||
},
|
||||
"123 > 0": {
|
||||
x: NewDecFromInt64(123),
|
||||
y: NewDecFromInt64(0),
|
||||
exp: false,
|
||||
},
|
||||
"-123 < 0": {
|
||||
x: NewDecFromInt64(-123),
|
||||
y: NewDecFromInt64(0),
|
||||
exp: true,
|
||||
},
|
||||
"123 == 123": {
|
||||
x: NewDecFromInt64(123),
|
||||
y: NewDecFromInt64(123),
|
||||
exp: false,
|
||||
},
|
||||
"-123 == -123": {
|
||||
x: NewDecFromInt64(-123),
|
||||
y: NewDecFromInt64(-123),
|
||||
exp: false,
|
||||
},
|
||||
"1.234 == 1.234": {
|
||||
x: NewDecWithExp(1234, -3),
|
||||
y: NewDecWithExp(1234, -3),
|
||||
exp: false,
|
||||
},
|
||||
"1.234 > 1.233": {
|
||||
x: NewDecWithExp(1234, -3),
|
||||
y: NewDecWithExp(1233, -3),
|
||||
exp: false,
|
||||
},
|
||||
"1.233 < 1.234": {
|
||||
x: NewDecWithExp(1233, -3),
|
||||
y: NewDecWithExp(1234, -3),
|
||||
exp: true,
|
||||
},
|
||||
}
|
||||
for name, spec := range specs {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
got := spec.x.LT(spec.y)
|
||||
assert.Equal(t, spec.exp, got, "x: %s, y: %s", spec.x.String(), spec.y.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLTE(t *testing.T) {
|
||||
specs := map[string]struct {
|
||||
x Dec
|
||||
y Dec
|
||||
exp bool
|
||||
}{
|
||||
"0 == 0": {
|
||||
x: NewDecFromInt64(0),
|
||||
y: NewDecFromInt64(0),
|
||||
exp: true,
|
||||
},
|
||||
"0 < 123": {
|
||||
x: NewDecFromInt64(0),
|
||||
y: NewDecFromInt64(123),
|
||||
exp: true,
|
||||
},
|
||||
"123 > 0": {
|
||||
x: NewDecFromInt64(123),
|
||||
y: NewDecFromInt64(0),
|
||||
exp: false,
|
||||
},
|
||||
"-123 < 0": {
|
||||
x: NewDecFromInt64(-123),
|
||||
y: NewDecFromInt64(0),
|
||||
exp: true,
|
||||
},
|
||||
"123 == 123": {
|
||||
x: NewDecFromInt64(123),
|
||||
y: NewDecFromInt64(123),
|
||||
exp: true,
|
||||
},
|
||||
"-123 == -123": {
|
||||
x: NewDecFromInt64(-123),
|
||||
y: NewDecFromInt64(-123),
|
||||
exp: true,
|
||||
},
|
||||
"1.234 == 1.234": {
|
||||
x: NewDecWithExp(1234, -3),
|
||||
y: NewDecWithExp(1234, -3),
|
||||
exp: true,
|
||||
},
|
||||
"1.234 > 1.233": {
|
||||
x: NewDecWithExp(1234, -3),
|
||||
y: NewDecWithExp(1233, -3),
|
||||
exp: false,
|
||||
},
|
||||
"1.233 < 1.234": {
|
||||
x: NewDecWithExp(1233, -3),
|
||||
y: NewDecWithExp(1234, -3),
|
||||
exp: true,
|
||||
},
|
||||
}
|
||||
for name, spec := range specs {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
got := spec.x.LTE(spec.y)
|
||||
assert.Equal(t, spec.exp, got, "x: %s, y: %s", spec.x.String(), spec.y.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGT(t *testing.T) {
|
||||
specs := map[string]struct {
|
||||
x Dec
|
||||
y Dec
|
||||
exp bool
|
||||
}{
|
||||
"0 == 0": {
|
||||
x: NewDecFromInt64(0),
|
||||
y: NewDecFromInt64(0),
|
||||
exp: false,
|
||||
},
|
||||
"0 < 123": {
|
||||
x: NewDecFromInt64(0),
|
||||
y: NewDecFromInt64(123),
|
||||
exp: false,
|
||||
},
|
||||
"123 > 0": {
|
||||
x: NewDecFromInt64(123),
|
||||
y: NewDecFromInt64(0),
|
||||
exp: true,
|
||||
},
|
||||
"-123 < 0": {
|
||||
x: NewDecFromInt64(-123),
|
||||
y: NewDecFromInt64(0),
|
||||
exp: false,
|
||||
},
|
||||
"123 == 123": {
|
||||
x: NewDecFromInt64(123),
|
||||
y: NewDecFromInt64(123),
|
||||
exp: false,
|
||||
},
|
||||
"-123 == -123": {
|
||||
x: NewDecFromInt64(-123),
|
||||
y: NewDecFromInt64(-123),
|
||||
exp: false,
|
||||
},
|
||||
"1.234 == 1.234": {
|
||||
x: NewDecWithExp(1234, -3),
|
||||
y: NewDecWithExp(1234, -3),
|
||||
exp: false,
|
||||
},
|
||||
"1.234 > 1.233": {
|
||||
x: NewDecWithExp(1234, -3),
|
||||
y: NewDecWithExp(1233, -3),
|
||||
exp: true,
|
||||
},
|
||||
"1.233 < 1.234": {
|
||||
x: NewDecWithExp(1233, -3),
|
||||
y: NewDecWithExp(1234, -3),
|
||||
exp: false,
|
||||
},
|
||||
}
|
||||
for name, spec := range specs {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
got := spec.x.GT(spec.y)
|
||||
assert.Equal(t, spec.exp, got, "x: %s, y: %s", spec.x.String(), spec.y.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGTE(t *testing.T) {
|
||||
specs := map[string]struct {
|
||||
x Dec
|
||||
y Dec
|
||||
exp bool
|
||||
}{
|
||||
"0 == 0": {
|
||||
x: NewDecFromInt64(0),
|
||||
y: NewDecFromInt64(0),
|
||||
exp: true,
|
||||
},
|
||||
"0 < 123": {
|
||||
x: NewDecFromInt64(0),
|
||||
y: NewDecFromInt64(123),
|
||||
exp: false,
|
||||
},
|
||||
"123 > 0": {
|
||||
x: NewDecFromInt64(123),
|
||||
y: NewDecFromInt64(0),
|
||||
exp: true,
|
||||
},
|
||||
"-123 < 0": {
|
||||
x: NewDecFromInt64(-123),
|
||||
y: NewDecFromInt64(0),
|
||||
exp: false,
|
||||
},
|
||||
"123 == 123": {
|
||||
x: NewDecFromInt64(123),
|
||||
y: NewDecFromInt64(123),
|
||||
exp: true,
|
||||
},
|
||||
"-123 == -123": {
|
||||
x: NewDecFromInt64(-123),
|
||||
y: NewDecFromInt64(-123),
|
||||
exp: true,
|
||||
},
|
||||
"1.234 == 1.234": {
|
||||
x: NewDecWithExp(1234, -3),
|
||||
y: NewDecWithExp(1234, -3),
|
||||
exp: true,
|
||||
},
|
||||
"1.234 > 1.233": {
|
||||
x: NewDecWithExp(1234, -3),
|
||||
y: NewDecWithExp(1233, -3),
|
||||
exp: true,
|
||||
},
|
||||
"1.233 < 1.234": {
|
||||
x: NewDecWithExp(1233, -3),
|
||||
y: NewDecWithExp(1234, -3),
|
||||
exp: false,
|
||||
},
|
||||
}
|
||||
for name, spec := range specs {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
got := spec.x.GTE(spec.y)
|
||||
assert.Equal(t, spec.exp, got, "x: %s, y: %s", spec.x.String(), spec.y.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMinDec(t *testing.T) {
|
||||
specs := map[string]struct {
|
||||
x Dec
|
||||
y Dec
|
||||
exp Dec
|
||||
}{
|
||||
"0 == 0": {
|
||||
x: NewDecFromInt64(0),
|
||||
y: NewDecFromInt64(0),
|
||||
exp: NewDecFromInt64(0),
|
||||
},
|
||||
"0 < 123": {
|
||||
x: NewDecFromInt64(0),
|
||||
y: NewDecFromInt64(123),
|
||||
exp: NewDecFromInt64(0),
|
||||
},
|
||||
"123 > 0": {
|
||||
x: NewDecFromInt64(123),
|
||||
y: NewDecFromInt64(0),
|
||||
exp: NewDecFromInt64(0),
|
||||
},
|
||||
"-123 < 0": {
|
||||
x: NewDecFromInt64(-123),
|
||||
y: NewDecFromInt64(0),
|
||||
exp: NewDecFromInt64(-123),
|
||||
},
|
||||
"123 == 123": {
|
||||
x: NewDecFromInt64(123),
|
||||
y: NewDecFromInt64(123),
|
||||
exp: NewDecFromInt64(123),
|
||||
},
|
||||
"-123 == -123": {
|
||||
x: NewDecFromInt64(-123),
|
||||
y: NewDecFromInt64(-123),
|
||||
exp: NewDecFromInt64(-123),
|
||||
},
|
||||
"1.234 == 1.234": {
|
||||
x: NewDecWithExp(1234, -3),
|
||||
y: NewDecWithExp(1234, -3),
|
||||
exp: NewDecWithExp(1234, -3),
|
||||
},
|
||||
"1.234 > 1.233": {
|
||||
x: NewDecWithExp(1234, -3),
|
||||
y: NewDecWithExp(1233, -3),
|
||||
exp: NewDecWithExp(1233, -3),
|
||||
},
|
||||
"1.233 < 1.234": {
|
||||
x: NewDecWithExp(1233, -3),
|
||||
y: NewDecWithExp(1234, -3),
|
||||
exp: NewDecWithExp(1233, -3),
|
||||
},
|
||||
}
|
||||
for name, spec := range specs {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
got := MinDec(spec.x, spec.y)
|
||||
assert.Equal(t, spec.exp, got, "x: %s, y: %s", spec.x.String(), spec.y.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMaxDec(t *testing.T) {
|
||||
specs := map[string]struct {
|
||||
x Dec
|
||||
y Dec
|
||||
exp Dec
|
||||
}{
|
||||
"0 == 0": {
|
||||
x: NewDecFromInt64(0),
|
||||
y: NewDecFromInt64(0),
|
||||
exp: NewDecFromInt64(0),
|
||||
},
|
||||
"0 < 123 ": {
|
||||
x: NewDecFromInt64(0),
|
||||
y: NewDecFromInt64(123),
|
||||
exp: NewDecFromInt64(123),
|
||||
},
|
||||
"123 > 0 ": {
|
||||
x: NewDecFromInt64(123),
|
||||
y: NewDecFromInt64(0),
|
||||
exp: NewDecFromInt64(123),
|
||||
},
|
||||
"-123 < 0": {
|
||||
x: NewDecFromInt64(-123),
|
||||
y: NewDecFromInt64(0),
|
||||
exp: NewDecFromInt64(0),
|
||||
},
|
||||
"123 == 123": {
|
||||
x: NewDecFromInt64(123),
|
||||
y: NewDecFromInt64(123),
|
||||
exp: NewDecFromInt64(123),
|
||||
},
|
||||
"-123 == -123": {
|
||||
x: NewDecFromInt64(-123),
|
||||
y: NewDecFromInt64(-123),
|
||||
exp: NewDecFromInt64(-123),
|
||||
},
|
||||
"1.234 == 1.234": {
|
||||
x: NewDecWithExp(1234, -3),
|
||||
y: NewDecWithExp(1234, -3),
|
||||
exp: NewDecWithExp(1234, -3),
|
||||
},
|
||||
"1.234 > 1.233": {
|
||||
x: NewDecWithExp(1234, -3),
|
||||
y: NewDecWithExp(1233, -3),
|
||||
exp: NewDecWithExp(1234, -3),
|
||||
},
|
||||
"1.233 < 1.234": {
|
||||
x: NewDecWithExp(1233, -3),
|
||||
y: NewDecWithExp(1234, -3),
|
||||
exp: NewDecWithExp(1234, -3),
|
||||
},
|
||||
}
|
||||
for name, spec := range specs {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
got := MaxDec(spec.x, spec.y)
|
||||
assert.Equal(t, spec.exp, got, "x: %s, y: %s", spec.x.String(), spec.y.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNeg(t *testing.T) {
|
||||
specs := map[string]struct {
|
||||
x Dec
|
||||
exp Dec
|
||||
expErr error
|
||||
}{
|
||||
"0": {
|
||||
x: NewDecFromInt64(0),
|
||||
exp: NewDecFromInt64(0),
|
||||
},
|
||||
"123": {
|
||||
x: NewDecFromInt64(123),
|
||||
exp: NewDecFromInt64(-123),
|
||||
},
|
||||
"-123": {
|
||||
x: NewDecFromInt64(-123),
|
||||
exp: NewDecFromInt64(123),
|
||||
},
|
||||
"1.234": {
|
||||
x: NewDecWithExp(1234, -3),
|
||||
exp: NewDecWithExp(-1234, -3),
|
||||
},
|
||||
"-1.234 ": {
|
||||
x: NewDecWithExp(-1234, -3),
|
||||
exp: NewDecWithExp(1234, -3),
|
||||
},
|
||||
|
||||
"1e100000": {
|
||||
x: NewDecWithExp(1, 100_000),
|
||||
exp: NewDecWithExp(-1, 100_000),
|
||||
},
|
||||
"-9e900000 -> Err": {
|
||||
x: NewDecWithExp(-9, 900_000),
|
||||
expErr: ErrInvalidDec,
|
||||
},
|
||||
"-1e^-1": {
|
||||
x: NewDecWithExp(-1, -1),
|
||||
exp: NewDecWithExp(1, -1),
|
||||
},
|
||||
"-1e100001": {
|
||||
x: NewDecWithExp(-1, 100_001),
|
||||
expErr: ErrInvalidDec,
|
||||
},
|
||||
"1e100001": {
|
||||
x: NewDecWithExp(1, 100_001),
|
||||
expErr: ErrInvalidDec,
|
||||
},
|
||||
}
|
||||
for name, spec := range specs {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
got, gotErr := spec.x.Neg()
|
||||
if spec.expErr != nil {
|
||||
require.ErrorIs(t, gotErr, spec.expErr, got)
|
||||
return
|
||||
}
|
||||
require.NoError(t, gotErr)
|
||||
assert.Equal(t, spec.exp, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAbs(t *testing.T) {
|
||||
specs := map[string]struct {
|
||||
x Dec
|
||||
exp Dec
|
||||
expErr error
|
||||
}{
|
||||
"0": {
|
||||
x: NewDecFromInt64(0),
|
||||
exp: NewDecFromInt64(0),
|
||||
},
|
||||
"123": {
|
||||
x: NewDecFromInt64(123),
|
||||
exp: NewDecFromInt64(123),
|
||||
},
|
||||
"-123": {
|
||||
x: NewDecFromInt64(-123),
|
||||
exp: NewDecFromInt64(123),
|
||||
},
|
||||
"1.234": {
|
||||
x: NewDecWithExp(1234, -3),
|
||||
exp: NewDecWithExp(1234, -3),
|
||||
},
|
||||
"-1.234 ": {
|
||||
x: NewDecWithExp(-1234, -3),
|
||||
exp: NewDecWithExp(1234, -3),
|
||||
},
|
||||
|
||||
"1e100000": {
|
||||
x: NewDecWithExp(1, 100_000),
|
||||
exp: NewDecWithExp(1, 100_000),
|
||||
},
|
||||
"-9e900000 -> Err": {
|
||||
x: NewDecWithExp(-9, 900_000),
|
||||
expErr: ErrInvalidDec,
|
||||
},
|
||||
"-1e^-1": {
|
||||
x: NewDecWithExp(-1, -1),
|
||||
exp: NewDecWithExp(1, -1),
|
||||
},
|
||||
"-1e100001": {
|
||||
x: NewDecWithExp(-1, 100_001),
|
||||
expErr: ErrInvalidDec,
|
||||
},
|
||||
"1e100001": {
|
||||
x: NewDecWithExp(1, 100_001),
|
||||
expErr: ErrInvalidDec,
|
||||
},
|
||||
}
|
||||
for name, spec := range specs {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
got, gotErr := spec.x.Abs()
|
||||
if spec.expErr != nil {
|
||||
require.ErrorIs(t, gotErr, spec.expErr, got)
|
||||
return
|
||||
}
|
||||
require.NoError(t, gotErr)
|
||||
assert.Equal(t, spec.exp, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user