NewFromString And NewFromFloat May Produce Different Decimals Given The Same Number
Due to how the exp is calculated on NewFromString and NewFromFloat, two different Decimals may be created given the same number.
package main
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/shopspring/decimal"
)
func Test(t *testing.T) {
n1, _ := decimal.NewFromString("10")
n2 := decimal.NewFromFloat(10)
assert.Equal(t, n1, n2, "equal numbers")
}
=== RUN Test Test: prog.go:14: Error Trace: prog.go:14 Error: Not equal: expected: decimal.Decimal{value:(*big.Int)(0xc00000e860), exp:0} actual : decimal.Decimal{value:(*big.Int)(0xc00000e8a0), exp:1}
Diff: Test: Test Messages: equal numbers--- FAIL: Test (0.00s) FAIL
https://play.golang.org/p/5mOcyDheifa
Thanks @marcocali for pointing this out. We will take a closer look on this :)
I have nearly the same issue with such code:
assert.Equal(t, decimal.Zero, decimal.NewFromFloat(0))
which fails because of the exp value:
Error: Not equal:
expected: decimal.Decimal{value:(*big.Int)(0xc00000e760), exp:1}
actual : decimal.Decimal{value:(*big.Int)(0xc00019bd80), exp:0}
I apologize for such late response @marcocali, @faide.
Yes, you are exactly right. Both NewFromFloat and NewFromString do not provide always 1:1 results. As semantically both functions return the same values, the underneath representation differs.
It is a known issue, and that's why you should always use Cmp or Equals method to compare two decimals. By using those methods you will be able to compare decimal values, not underneath struct values.
Anyhow, we will try to correct this behaviour, but it may be difficult and may take some time.
Is there a way to adjust the exp value that can be done before trying to compare?
this inconsistency problem brings some kind of trouble, for most assertions in unit tests, I can simply use assert.Equal(t, struct1, struct2) to judge if they are equal if the both don't include a decimal field.
Because of this problem, I have to assert each field one by one like:
assert.Equal(t, struct1.Field1, struct2.Field1)
assert.Equal(t, struct1.Field2, struct2.Field2)
...
assert.True(t, struct1.DecimalField.Equal(struct2.DecimalField))
Hope it can be solved.
May I know any update on this issue? this is caused us problem when we are writing test and test is crucial in software development. we can't simply use refelect.DeepEqual() or assert.ElementMatch() to check on our test.
Well,
this is not perfect but at least you can use:
assert.True(t, struct1.DecimalField.Equal(struct2.DecimalField))
as @Martin91 pointed out above.
The point here is to use decimal1.Equal(decimal2) or decimal1.Cmp(decimal2) before asserting with your test library