Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Modify Rounding methods #220

Merged
merged 3 commits into from Mar 14, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
59 changes: 54 additions & 5 deletions decimal.go
Expand Up @@ -892,6 +892,58 @@ func (d Decimal) Round(places int32) Decimal {
return ret
}

// 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 towards +infinity.
//
// Example:
Expand All @@ -913,6 +965,8 @@ 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
Expand All @@ -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
}

Expand Down
190 changes: 186 additions & 4 deletions decimal_test.go
Expand Up @@ -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
Expand Down Expand Up @@ -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)
}

Expand All @@ -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
Expand Down Expand Up @@ -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 {
Expand Down