From cd57bf11a46b966f33f140a3a194bf09c234c47a Mon Sep 17 00:00:00 2001 From: Gaylor Bosson Date: Wed, 20 Oct 2021 12:49:41 +0200 Subject: [PATCH 1/4] Fix binary marshalling of decimal zero value (#253) * Fix binary marshalling of zero value * Adapt the error message of BinaryUnmarshal --- decimal.go | 8 ++++---- decimal_test.go | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/decimal.go b/decimal.go index 7686af1..62edc05 100644 --- a/decimal.go +++ b/decimal.go @@ -1353,10 +1353,10 @@ func (d Decimal) MarshalJSON() ([]byte, error) { // UnmarshalBinary implements the encoding.BinaryUnmarshaler interface. As a string representation // is already used when encoding to text, this method stores that string as []byte func (d *Decimal) UnmarshalBinary(data []byte) error { - // Verify we have at least 5 bytes, 4 for the exponent and at least 1 more - // for the GOB encoded value. - if len(data) < 5 { - return fmt.Errorf("error decoding binary %v: expected at least 5 bytes, got %d", data, len(data)) + // Verify we have at least 4 bytes for the exponent. The GOB encoded value + // may be empty. + if len(data) < 4 { + return fmt.Errorf("error decoding binary %v: expected at least 4 bytes, got %d", data, len(data)) } // Extract the exponent diff --git a/decimal_test.go b/decimal_test.go index b53b68b..cd7f00b 100644 --- a/decimal_test.go +++ b/decimal_test.go @@ -3002,6 +3002,25 @@ func TestBinary(t *testing.T) { } } +func TestBinary_Zero(t *testing.T) { + var d1 Decimal + + b, err := d1.MarshalBinary() + if err != nil { + t.Fatalf("error marshalling %v to binary: %v", d1, err) + } + + var d2 Decimal + err = (&d2).UnmarshalBinary(b) + if err != nil { + t.Errorf("error unmarshalling from binary: %v", err) + } + + if !d1.Equals(d2) { + t.Errorf("expected %v when restoring, got %v", d1, d2) + } +} + func TestBinary_DataTooShort(t *testing.T) { var d Decimal From bc96f543c22f41098ca6615852dc3c4c55b3aa07 Mon Sep 17 00:00:00 2001 From: lmittmann Date: Wed, 20 Oct 2021 13:11:07 +0200 Subject: [PATCH 2/4] Reduce memory allocation in case of initialization from big.Int (#252) * reduced allocs in NewFromBigInt(...) from 3 -> 2 * remaining `big.NewInt(0).Set(...)` -> `new(big.Int).Set(...)` --- decimal.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/decimal.go b/decimal.go index 62edc05..84405ec 100644 --- a/decimal.go +++ b/decimal.go @@ -120,7 +120,7 @@ func NewFromInt32(value int32) Decimal { // NewFromBigInt returns a new Decimal from a big.Int, value * 10 ^ exp func NewFromBigInt(value *big.Int, exp int32) Decimal { return Decimal{ - value: big.NewInt(0).Set(value), + value: new(big.Int).Set(value), exp: exp, } } @@ -831,7 +831,7 @@ func (d Decimal) IsInteger() bool { // When the exponent is negative we have to check every number after the decimal place // If all of them are zeroes, we are sure that given decimal can be represented as an integer var r big.Int - q := big.NewInt(0).Set(d.value) + q := new(big.Int).Set(d.value) for z := abs(d.exp); z > 0; z-- { q.QuoRem(q, tenInt, &r) if r.Cmp(zeroInt) != 0 { @@ -949,7 +949,7 @@ func (d Decimal) Exponent() int32 { func (d Decimal) Coefficient() *big.Int { d.ensureInitialized() // we copy the coefficient so that mutating the result does not mutate the Decimal. - return big.NewInt(0).Set(d.value) + return new(big.Int).Set(d.value) } // CoefficientInt64 returns the coefficient of the decimal as int64. It is scaled by 10^Exponent() From 683f5e1a7e189020762b6ac3a0cd22276c136ee5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Wo=C5=9B?= Date: Wed, 20 Oct 2021 13:17:51 +0200 Subject: [PATCH 3/4] Release version 1.3.1 (#255) --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6339ff2..aea6115 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## Decimal v1.3.1 + +#### ENHANCEMENTS +- Reduce memory allocation in case of initialization from big.Int [#252](https://github.com/shopspring/decimal/pull/252) + +#### BUGFIXES +- Fix binary marshalling of decimal zero value [#253](https://github.com/shopspring/decimal/pull/253) + ## Decimal v1.3.0 #### FEATURES From fa3b22f4d484d626ee81919285cf3d22ad3a4000 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Wo=C5=9B?= Date: Thu, 21 Oct 2021 11:22:23 +0200 Subject: [PATCH 4/4] Fix incorrect calculation of decimal modulo (#258) --- decimal.go | 4 ++-- decimal_test.go | 20 ++++++++++++-------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/decimal.go b/decimal.go index 84405ec..c614ea7 100644 --- a/decimal.go +++ b/decimal.go @@ -552,7 +552,7 @@ func (d Decimal) Div(d2 Decimal) Decimal { return d.DivRound(d2, int32(DivisionPrecision)) } -// QuoRem does divsion with remainder +// QuoRem does division with remainder // d.QuoRem(d2,precision) returns quotient q and remainder r such that // d = d2 * q + r, q an integer multiple of 10^(-precision) // 0 <= r < abs(d2) * 10 ^(-precision) if d>=0 @@ -628,7 +628,7 @@ func (d Decimal) DivRound(d2 Decimal, precision int32) Decimal { // Mod returns d % d2. func (d Decimal) Mod(d2 Decimal) Decimal { - quo := d.Div(d2).Truncate(0) + quo := d.DivRound(d2, -d.exp+1).Truncate(0) return d.Sub(d2.Mul(quo)) } diff --git a/decimal_test.go b/decimal_test.go index cd7f00b..2b3a99e 100644 --- a/decimal_test.go +++ b/decimal_test.go @@ -2131,14 +2131,18 @@ func TestDecimal_Mod(t *testing.T) { } inputs := map[Inp]string{ - Inp{"3", "2"}: "1", - Inp{"3451204593", "2454495034"}: "996709559", - Inp{"24544.95034", ".3451204593"}: "0.3283950433", - Inp{".1", ".1"}: "0", - Inp{"0", "1.001"}: "0", - Inp{"-7.5", "2"}: "-1.5", - Inp{"7.5", "-2"}: "1.5", - Inp{"-7.5", "-2"}: "-1.5", + Inp{"3", "2"}: "1", + Inp{"3451204593", "2454495034"}: "996709559", + Inp{"9999999999", "1275"}: "324", + Inp{"9999999999.9999998", "1275.49"}: "239.2399998", + Inp{"24544.95034", "0.3451204593"}: "0.3283950433", + Inp{"0.499999999999999999", "0.25"}: "0.249999999999999999", + Inp{"0.989512958912895912", "0.000001"}: "0.000000958912895912", + Inp{"0.1", "0.1"}: "0", + Inp{"0", "1.001"}: "0", + Inp{"-7.5", "2"}: "-1.5", + Inp{"7.5", "-2"}: "1.5", + Inp{"-7.5", "-2"}: "-1.5", } for inp, res := range inputs {