diff --git a/decimal.go b/decimal.go index e68ad2c8..db4efc6f 100644 --- a/decimal.go +++ b/decimal.go @@ -892,7 +892,59 @@ func (d Decimal) Round(places int32) Decimal { return ret } -// RoundUp rounds the decimal towards +infinity. +// RoundCeil rounds the decimal towards +infinity. +// +// Example: +// +// NewFromFloat(545).RoundCeil(-2).String() // output: "600" +// NewFromFloat(500).RoundCeil(-2).String() // output: "500" +// NewFromFloat(1.1001).RoundCeil(2).String() // output: "1.11" +// NewFromFloat(-1.454).RoundCeil(1).String() // output: "-1.5" +// +func (d Decimal) RoundCeil(places int32) Decimal { + if d.exp >= -places { + return d + } + + rescaled := d.rescale(-places) + if d.Equal(rescaled) { + return d + } + + if d.value.Sign() > 0 { + rescaled.value.Add(rescaled.value, oneInt) + } + + return rescaled +} + +// RoundFloor rounds the decimal towards -infinity. +// +// Example: +// +// NewFromFloat(545).RoundFloor(-2).String() // output: "500" +// NewFromFloat(-500).RoundFloor(-2).String() // output: "-500" +// NewFromFloat(1.1001).RoundFloor(2).String() // output: "1.1" +// NewFromFloat(-1.454).RoundFloor(1).String() // output: "-1.4" +// +func (d Decimal) RoundFloor(places int32) Decimal { + if d.exp >= -places { + return d + } + + rescaled := d.rescale(-places) + if d.Equal(rescaled) { + return d + } + + if d.value.Sign() < 0 { + rescaled.value.Sub(rescaled.value, oneInt) + } + + return rescaled +} + +// RoundUp rounds the decimal away from zero. // // Example: // @@ -913,12 +965,14 @@ func (d Decimal) RoundUp(places int32) Decimal { if d.value.Sign() > 0 { rescaled.value.Add(rescaled.value, oneInt) + } else if d.value.Sign() < 0 { + rescaled.value.Sub(rescaled.value, oneInt) } return rescaled } -// RoundDown rounds the decimal towards -infinity. +// RoundDown rounds the decimal towards zero. // // Example: // @@ -936,11 +990,6 @@ func (d Decimal) RoundDown(places int32) Decimal { if d.Equal(rescaled) { return d } - - if d.value.Sign() < 0 { - rescaled.value.Sub(rescaled.value, oneInt) - } - return rescaled } diff --git a/decimal_test.go b/decimal_test.go index 1bfca281..36677548 100644 --- a/decimal_test.go +++ b/decimal_test.go @@ -1084,7 +1084,7 @@ func TestDecimal_RoundAndStringFixed(t *testing.T) { } } -func TestDecimal_RoundUpAndStringFixed(t *testing.T) { +func TestDecimal_RoundCeilAndStringFixed(t *testing.T) { type testData struct { input string places int32 @@ -1157,9 +1157,9 @@ func TestDecimal_RoundUpAndStringFixed(t *testing.T) { if err != nil { t.Fatal(err) } - got := d.RoundUp(test.places) + got := d.RoundCeil(test.places) if !got.Equal(expected) { - t.Errorf("Rounding up %s to %d places, got %s, expected %s", + t.Errorf("Rounding ceil %s to %d places, got %s, expected %s", d, test.places, got, expected) } @@ -1175,7 +1175,7 @@ func TestDecimal_RoundUpAndStringFixed(t *testing.T) { } } -func TestDecimal_RoundDownAndStringFixed(t *testing.T) { +func TestDecimal_RoundFloorAndStringFixed(t *testing.T) { type testData struct { input string places int32 @@ -1237,6 +1237,188 @@ func TestDecimal_RoundDownAndStringFixed(t *testing.T) { {"-499", -4, "-10000", ""}, } + for _, test := range tests { + d, err := NewFromString(test.input) + if err != nil { + t.Fatal(err) + } + + // test Round + expected, err := NewFromString(test.expected) + if err != nil { + t.Fatal(err) + } + got := d.RoundFloor(test.places) + if !got.Equal(expected) { + t.Errorf("Rounding floor %s to %d places, got %s, expected %s", + d, test.places, got, expected) + } + + // test StringFixed + if test.expectedFixed == "" { + test.expectedFixed = test.expected + } + gotStr := got.StringFixed(test.places) + if gotStr != test.expectedFixed { + t.Errorf("(%s).StringFixed(%d): got %s, expected %s", + d, test.places, gotStr, test.expectedFixed) + } + } +} + +func TestDecimal_RoundUpAndStringFixed(t *testing.T) { + type testData struct { + input string + places int32 + expected string + expectedFixed string + } + tests := []testData{ + {"1.454", 0, "2", ""}, + {"1.454", 1, "1.5", ""}, + {"1.454", 2, "1.46", ""}, + {"1.454", 3, "1.454", ""}, + {"1.454", 4, "1.454", "1.4540"}, + {"1.454", 5, "1.454", "1.45400"}, + {"1.554", 0, "2", ""}, + {"1.554", 1, "1.6", ""}, + {"1.554", 2, "1.56", ""}, + {"0.554", 0, "1", ""}, + {"0.454", 0, "1", ""}, + {"0.454", 5, "0.454", "0.45400"}, + {"0", 0, "0", ""}, + {"0", 1, "0", "0.0"}, + {"0", 2, "0", "0.00"}, + {"0", -1, "0", ""}, + {"5", 2, "5", "5.00"}, + {"5", 1, "5", "5.0"}, + {"5", 0, "5", ""}, + {"500", 2, "500", "500.00"}, + {"500", -2, "500", ""}, + {"545", -1, "550", ""}, + {"545", -2, "600", ""}, + {"545", -3, "1000", ""}, + {"545", -4, "10000", ""}, + {"499", -3, "1000", ""}, + {"499", -4, "10000", ""}, + {"1.1001", 2, "1.11", ""}, + {"-1.1001", 2, "-1.11", ""}, + {"-1.454", 0, "-2", ""}, + {"-1.454", 1, "-1.5", ""}, + {"-1.454", 2, "-1.46", ""}, + {"-1.454", 3, "-1.454", ""}, + {"-1.454", 4, "-1.454", "-1.4540"}, + {"-1.454", 5, "-1.454", "-1.45400"}, + {"-1.554", 0, "-2", ""}, + {"-1.554", 1, "-1.6", ""}, + {"-1.554", 2, "-1.56", ""}, + {"-0.554", 0, "-1", ""}, + {"-0.454", 0, "-1", ""}, + {"-0.454", 5, "-0.454", "-0.45400"}, + {"-5", 2, "-5", "-5.00"}, + {"-5", 1, "-5", "-5.0"}, + {"-5", 0, "-5", ""}, + {"-500", 2, "-500", "-500.00"}, + {"-500", -2, "-500", ""}, + {"-545", -1, "-550", ""}, + {"-545", -2, "-600", ""}, + {"-545", -3, "-1000", ""}, + {"-545", -4, "-10000", ""}, + {"-499", -3, "-1000", ""}, + {"-499", -4, "-10000", ""}, + } + + for _, test := range tests { + d, err := NewFromString(test.input) + if err != nil { + t.Fatal(err) + } + + // test Round + expected, err := NewFromString(test.expected) + if err != nil { + t.Fatal(err) + } + got := d.RoundUp(test.places) + if !got.Equal(expected) { + t.Errorf("Rounding up %s to %d places, got %s, expected %s", + d, test.places, got, expected) + } + + // test StringFixed + if test.expectedFixed == "" { + test.expectedFixed = test.expected + } + gotStr := got.StringFixed(test.places) + if gotStr != test.expectedFixed { + t.Errorf("(%s).StringFixed(%d): got %s, expected %s", + d, test.places, gotStr, test.expectedFixed) + } + } +} + +func TestDecimal_RoundDownAndStringFixed(t *testing.T) { + type testData struct { + input string + places int32 + expected string + expectedFixed string + } + tests := []testData{ + {"1.454", 0, "1", ""}, + {"1.454", 1, "1.4", ""}, + {"1.454", 2, "1.45", ""}, + {"1.454", 3, "1.454", ""}, + {"1.454", 4, "1.454", "1.4540"}, + {"1.454", 5, "1.454", "1.45400"}, + {"1.554", 0, "1", ""}, + {"1.554", 1, "1.5", ""}, + {"1.554", 2, "1.55", ""}, + {"0.554", 0, "0", ""}, + {"0.454", 0, "0", ""}, + {"0.454", 5, "0.454", "0.45400"}, + {"0", 0, "0", ""}, + {"0", 1, "0", "0.0"}, + {"0", 2, "0", "0.00"}, + {"0", -1, "0", ""}, + {"5", 2, "5", "5.00"}, + {"5", 1, "5", "5.0"}, + {"5", 0, "5", ""}, + {"500", 2, "500", "500.00"}, + {"500", -2, "500", ""}, + {"545", -1, "540", ""}, + {"545", -2, "500", ""}, + {"545", -3, "0", ""}, + {"545", -4, "0", ""}, + {"499", -3, "0", ""}, + {"499", -4, "0", ""}, + {"1.1001", 2, "1.10", ""}, + {"-1.1001", 2, "-1.10", ""}, + {"-1.454", 0, "-1", ""}, + {"-1.454", 1, "-1.4", ""}, + {"-1.454", 2, "-1.45", ""}, + {"-1.454", 3, "-1.454", ""}, + {"-1.454", 4, "-1.454", "-1.4540"}, + {"-1.454", 5, "-1.454", "-1.45400"}, + {"-1.554", 0, "-1", ""}, + {"-1.554", 1, "-1.5", ""}, + {"-1.554", 2, "-1.55", ""}, + {"-0.554", 0, "0", ""}, + {"-0.454", 0, "0", ""}, + {"-0.454", 5, "-0.454", "-0.45400"}, + {"-5", 2, "-5", "-5.00"}, + {"-5", 1, "-5", "-5.0"}, + {"-5", 0, "-5", ""}, + {"-500", 2, "-500", "-500.00"}, + {"-500", -2, "-500", ""}, + {"-545", -1, "-540", ""}, + {"-545", -2, "-500", ""}, + {"-545", -3, "0", ""}, + {"-545", -4, "0", ""}, + {"-499", -3, "0", ""}, + {"-499", -4, "0", ""}, + } + for _, test := range tests { d, err := NewFromString(test.input) if err != nil {