aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRigel <rigel.rozanski@gmail.com>2017-09-11 20:58:34 +0800
committerVictor Quinn <mail@victorquinn.com>2017-09-11 20:58:34 +0800
commitaed1bfe463fa3c9cc268d60dcc1491db613bff7e (patch)
tree24efb66387763d59ed423bb8e35898a55808bb98
parent730de27aa507d979cb5a8a17b11c82f361c3d4a3 (diff)
downloaddexon-decimal-aed1bfe463fa3c9cc268d60dcc1491db613bff7e.tar.gz
dexon-decimal-aed1bfe463fa3c9cc268d60dcc1491db613bff7e.tar.zst
dexon-decimal-aed1bfe463fa3c9cc268d60dcc1491db613bff7e.zip
Added Banker Rounding (#61)
* made GTE/GT/LT/LTE go-lint compliant * added banker rounding and tests (RoundBank, StringFixedBank)
-rw-r--r--decimal.go57
-rw-r--r--decimal_test.go84
2 files changed, 137 insertions, 4 deletions
diff --git a/decimal.go b/decimal.go
index 17c8d72..1729b61 100644
--- a/decimal.go
+++ b/decimal.go
@@ -431,23 +431,23 @@ func (d Decimal) Equals(d2 Decimal) bool {
return d.Equal(d2)
}
-// Greater Than (GT) returns true when d is greater than d2.
+// GreaterThan (GT) returns true when d is greater than d2.
func (d Decimal) GreaterThan(d2 Decimal) bool {
return d.Cmp(d2) == 1
}
-// Greater Than or Equal (GTE) returns true when d is greater than or equal to d2.
+// GreaterThanOrEqual (GTE) returns true when d is greater than or equal to d2.
func (d Decimal) GreaterThanOrEqual(d2 Decimal) bool {
cmp := d.Cmp(d2)
return cmp == 1 || cmp == 0
}
-// Less Than (LT) returns true when d is less than d2.
+// LessThan (LT) returns true when d is less than d2.
func (d Decimal) LessThan(d2 Decimal) bool {
return d.Cmp(d2) == -1
}
-// Less Than or Equal (LTE) returns true when d is less than or equal to d2.
+// LessThanOrEqual (LTE) returns true when d is less than or equal to d2.
func (d Decimal) LessThanOrEqual(d2 Decimal) bool {
cmp := d.Cmp(d2)
return cmp == -1 || cmp == 0
@@ -539,6 +539,24 @@ func (d Decimal) StringFixed(places int32) string {
return rounded.string(false)
}
+// StringFixedBank returns a banker rounded fixed-point string with places digits
+// after the decimal point.
+//
+// Example:
+//
+// NewFromFloat(0).StringFixed(2) // output: "0.00"
+// NewFromFloat(0).StringFixed(0) // output: "0"
+// NewFromFloat(5.45).StringFixed(0) // output: "5"
+// NewFromFloat(5.45).StringFixed(1) // output: "5.4"
+// NewFromFloat(5.45).StringFixed(2) // output: "5.45"
+// NewFromFloat(5.45).StringFixed(3) // output: "5.450"
+// NewFromFloat(545).StringFixed(-1) // output: "550"
+//
+func (d Decimal) StringFixedBank(places int32) string {
+ rounded := d.RoundBank(places)
+ return rounded.string(false)
+}
+
// Round rounds the decimal to places decimal places.
// If places < 0, it will round the integer part to the nearest 10^(-places).
//
@@ -568,6 +586,37 @@ func (d Decimal) Round(places int32) Decimal {
return ret
}
+// RoundBank rounds the decimal to places decimal places.
+// If the final digit to round is equidistant from the nearest two integers the
+// rounded value is taken as the even number
+//
+// If places < 0, it will round the integer part to the nearest 10^(-places).
+//
+// Examples:
+//
+// NewFromFloat(5.45).Round(1).String() // output: "5.4"
+// NewFromFloat(545).Round(-1).String() // output: "540"
+// NewFromFloat(5.46).Round(1).String() // output: "5.5"
+// NewFromFloat(546).Round(-1).String() // output: "550"
+// NewFromFloat(5.55).Round(1).String() // output: "5.6"
+// NewFromFloat(555).Round(-1).String() // output: "560"
+//
+func (d Decimal) RoundBank(places int32) Decimal {
+
+ round := d.Round(places)
+ remainder := d.Sub(round).Abs()
+
+ if remainder.value.Cmp(fiveInt) == 0 && round.value.Bit(0) != 0 {
+ if round.value.Sign() < 0 {
+ round.value.Add(round.value, oneInt)
+ } else {
+ round.value.Sub(round.value, oneInt)
+ }
+ }
+
+ return round
+}
+
// Floor returns the nearest integer value less than or equal to d.
func (d Decimal) Floor() Decimal {
d.ensureInitialized()
diff --git a/decimal_test.go b/decimal_test.go
index fc9cb12..d606dc9 100644
--- a/decimal_test.go
+++ b/decimal_test.go
@@ -550,6 +550,90 @@ func TestDecimal_RoundAndStringFixed(t *testing.T) {
}
}
+func TestDecimal_BankRoundAndStringFixed(t *testing.T) {
+ type testData struct {
+ input string
+ places int32
+ expected string
+ expectedFixed string
+ }
+ tests := []testData{
+ {"1.454", 0, "1", ""},
+ {"1.454", 1, "1.5", ""},
+ {"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, "2", ""},
+ {"1.554", 1, "1.6", ""},
+ {"1.554", 2, "1.55", ""},
+ {"0.554", 0, "1", ""},
+ {"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"},
+ {"545", -2, "500", ""},
+ {"545", -3, "1000", ""},
+ {"545", -4, "0", ""},
+ {"499", -3, "0", ""},
+ {"499", -4, "0", ""},
+ {"1.45", 1, "1.4", ""},
+ {"1.55", 1, "1.6", ""},
+ {"1.65", 1, "1.6", ""},
+ {"545", -1, "540", ""},
+ {"565", -1, "560", ""},
+ {"555", -1, "560", ""},
+ }
+
+ // add negative number tests
+ for _, test := range tests {
+ expected := test.expected
+ if expected != "0" {
+ expected = "-" + expected
+ }
+ expectedStr := test.expectedFixed
+ if strings.ContainsAny(expectedStr, "123456789") && expectedStr != "" {
+ expectedStr = "-" + expectedStr
+ }
+ tests = append(tests,
+ testData{"-" + test.input, test.places, expected, expectedStr})
+ }
+
+ for _, test := range tests {
+ d, err := NewFromString(test.input)
+ if err != nil {
+ panic(err)
+ }
+
+ // test Round
+ expected, err := NewFromString(test.expected)
+ if err != nil {
+ panic(err)
+ }
+ got := d.RoundBank(test.places)
+ if !got.Equal(expected) {
+ t.Errorf("Bank Rounding %s to %d places, got %s, expected %s",
+ d, test.places, got, expected)
+ }
+
+ // test StringFixed
+ if test.expectedFixed == "" {
+ test.expectedFixed = test.expected
+ }
+ gotStr := d.StringFixedBank(test.places)
+ if gotStr != test.expectedFixed {
+ t.Errorf("(%s).StringFixed(%d): got %s, expected %s",
+ d, test.places, gotStr, test.expectedFixed)
+ }
+ }
+}
+
func TestDecimal_Uninitialized(t *testing.T) {
a := Decimal{}
b := Decimal{}