mirror of
https://github.com/shopspring/decimal.git
synced 2024-11-22 20:40:48 +01:00
Fix Floor() and Ceil() for integer values (#64)
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.
This commit is contained in:
parent
faaed5fca7
commit
9ca7f51822
2 changed files with 96 additions and 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
|
||||
|
|
100
decimal_test.go
100
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue