diff options
author | tbrs <tbrs@users.noreply.github.com> | 2017-11-09 06:52:54 +0800 |
---|---|---|
committer | Victor Quinn <mail@victorquinn.com> | 2017-11-09 06:52:54 +0800 |
commit | 9ca7f51822d222ae4e246f070f9aad863599bd1a (patch) | |
tree | a08e4fc6f7f53de018a492e3cf5bbb9215653057 | |
parent | faaed5fca71709f7bb4e0b0e564089c7a4a1b3a8 (diff) | |
download | dexon-decimal-9ca7f51822d222ae4e246f070f9aad863599bd1a.tar.gz dexon-decimal-9ca7f51822d222ae4e246f070f9aad863599bd1a.tar.zst dexon-decimal-9ca7f51822d222ae4e246f070f9aad863599bd1a.zip |
Fix Floor() and Ceil() for integer values (#64)1.0.0
Implementation of aforementioned methods applied an integer multiplier
to int part, and rejected the exponent. This did not work well for
positive exponent values - multiplier was supposed to be a non-integer
value less than 1, but was rounded up to it. This caused different
results for equal Decimal values like decimal.New(19, 1) and
decimal.New(1900, -1).
Now functions return the receiver if it represents an integer value.
This also reduces execution time for previously broken cases.
-rw-r--r-- | decimal.go | 8 | ||||
-rw-r--r-- | decimal_test.go | 104 |
2 files changed, 96 insertions, 16 deletions
@@ -682,6 +682,10 @@ func (d Decimal) RoundCash(interval uint8) Decimal { func (d Decimal) Floor() Decimal { d.ensureInitialized() + if d.exp >= 0 { + return d + } + exp := big.NewInt(10) // NOTE(vadim): must negate after casting to prevent int32 overflow @@ -695,6 +699,10 @@ func (d Decimal) Floor() Decimal { func (d Decimal) Ceil() Decimal { d.ensureInitialized() + if d.exp >= 0 { + return d + } + exp := big.NewInt(10) // NOTE(vadim): must negate after casting to prevent int32 overflow diff --git a/decimal_test.go b/decimal_test.go index 8476bed..ecb24ba 100644 --- a/decimal_test.go +++ b/decimal_test.go @@ -508,11 +508,17 @@ func TestDecimal_rescale(t *testing.T) { } func TestDecimal_Floor(t *testing.T) { - type testData struct { + assertFloor := func(input, expected Decimal) { + got := input.Floor() + if !got.Equal(expected) { + t.Errorf("Floor(%s): got %s, expected %s", input, got, expected) + } + } + type testDataString struct { input string expected string } - tests := []testData{ + testsWithStrings := []testDataString{ {"1.999", "1"}, {"1", "1"}, {"1.01", "1"}, @@ -525,22 +531,66 @@ func TestDecimal_Floor(t *testing.T) { {"-1.01", "-2"}, {"-1.999", "-2"}, } - for _, test := range tests { - d, _ := NewFromString(test.input) + for _, test := range testsWithStrings { expected, _ := NewFromString(test.expected) - got := d.Floor() - if !got.Equal(expected) { - t.Errorf("Floor(%s): got %s, expected %s", d, got, expected) - } + input, _ := NewFromString(test.input) + assertFloor(input, expected) + } + + type testDataDecimal struct { + input Decimal + expected string + } + testsWithDecimals := []testDataDecimal{ + {New(100, -1), "10"}, + {New(10, 0), "10"}, + {New(1, 1), "10"}, + {New(1999, -3), "1"}, + {New(101, -2), "1"}, + {New(1, 0), "1"}, + {New(0, 0), "0"}, + {New(9, -1), "0"}, + {New(1, -1), "0"}, + {New(-1, -1), "-1"}, + {New(-9, -1), "-1"}, + {New(-1, 0), "-1"}, + {New(-101, -2), "-2"}, + {New(-1999, -3), "-2"}, + } + for _, test := range testsWithDecimals { + expected, _ := NewFromString(test.expected) + assertFloor(test.input, expected) + } +} + +func Benchmark_FloorFast(b *testing.B) { + input := New(200, 2) + b.ResetTimer() + for i := 0; i < b.N; i++ { + input.Floor() + } +} + +func Benchmark_FloorRegular(b *testing.B) { + input := New(200, -2) + b.ResetTimer() + for i := 0; i < b.N; i++ { + input.Floor() } } func TestDecimal_Ceil(t *testing.T) { - type testData struct { + assertCeil := func(input, expected Decimal) { + got := input.Ceil() + if !got.Equal(expected) { + t.Errorf("Ceil(%s): got %s, expected %s", input, got, expected) + } + } + type testDataString struct { input string expected string } - tests := []testData{ + testsWithStrings := []testDataString{ {"1.999", "2"}, {"1", "1"}, {"1.01", "2"}, @@ -553,13 +603,35 @@ func TestDecimal_Ceil(t *testing.T) { {"-1.01", "-1"}, {"-1.999", "-1"}, } - for _, test := range tests { - d, _ := NewFromString(test.input) + for _, test := range testsWithStrings { expected, _ := NewFromString(test.expected) - got := d.Ceil() - if !got.Equal(expected) { - t.Errorf("Ceil(%s): got %s, expected %s", d, got, expected) - } + input, _ := NewFromString(test.input) + assertCeil(input, expected) + } + + type testDataDecimal struct { + input Decimal + expected string + } + testsWithDecimals := []testDataDecimal{ + {New(100, -1), "10"}, + {New(10, 0), "10"}, + {New(1, 1), "10"}, + {New(1999, -3), "2"}, + {New(101, -2), "2"}, + {New(1, 0), "1"}, + {New(0, 0), "0"}, + {New(9, -1), "1"}, + {New(1, -1), "1"}, + {New(-1, -1), "0"}, + {New(-9, -1), "0"}, + {New(-1, 0), "-1"}, + {New(-101, -2), "-1"}, + {New(-1999, -3), "-1"}, + } + for _, test := range testsWithDecimals { + expected, _ := NewFromString(test.expected) + assertCeil(test.input, expected) } } |