aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortbrs <tbrs@users.noreply.github.com>2017-11-09 06:52:54 +0800
committerVictor Quinn <mail@victorquinn.com>2017-11-09 06:52:54 +0800
commit9ca7f51822d222ae4e246f070f9aad863599bd1a (patch)
treea08e4fc6f7f53de018a492e3cf5bbb9215653057
parentfaaed5fca71709f7bb4e0b0e564089c7a4a1b3a8 (diff)
downloaddexon-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.go8
-rw-r--r--decimal_test.go104
2 files changed, 96 insertions, 16 deletions
diff --git a/decimal.go b/decimal.go
index a78e390..1d9399c 100644
--- a/decimal.go
+++ b/decimal.go
@@ -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)
}
}