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 {
|
func (d Decimal) Floor() Decimal {
|
||||||
d.ensureInitialized()
|
d.ensureInitialized()
|
||||||
|
|
||||||
|
if d.exp >= 0 {
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
exp := big.NewInt(10)
|
exp := big.NewInt(10)
|
||||||
|
|
||||||
// NOTE(vadim): must negate after casting to prevent int32 overflow
|
// NOTE(vadim): must negate after casting to prevent int32 overflow
|
||||||
|
@ -695,6 +699,10 @@ func (d Decimal) Floor() Decimal {
|
||||||
func (d Decimal) Ceil() Decimal {
|
func (d Decimal) Ceil() Decimal {
|
||||||
d.ensureInitialized()
|
d.ensureInitialized()
|
||||||
|
|
||||||
|
if d.exp >= 0 {
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
exp := big.NewInt(10)
|
exp := big.NewInt(10)
|
||||||
|
|
||||||
// NOTE(vadim): must negate after casting to prevent int32 overflow
|
// NOTE(vadim): must negate after casting to prevent int32 overflow
|
||||||
|
|
104
decimal_test.go
104
decimal_test.go
|
@ -508,11 +508,17 @@ func TestDecimal_rescale(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDecimal_Floor(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
|
input string
|
||||||
expected string
|
expected string
|
||||||
}
|
}
|
||||||
tests := []testData{
|
testsWithStrings := []testDataString{
|
||||||
{"1.999", "1"},
|
{"1.999", "1"},
|
||||||
{"1", "1"},
|
{"1", "1"},
|
||||||
{"1.01", "1"},
|
{"1.01", "1"},
|
||||||
|
@ -525,22 +531,66 @@ func TestDecimal_Floor(t *testing.T) {
|
||||||
{"-1.01", "-2"},
|
{"-1.01", "-2"},
|
||||||
{"-1.999", "-2"},
|
{"-1.999", "-2"},
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range testsWithStrings {
|
||||||
d, _ := NewFromString(test.input)
|
|
||||||
expected, _ := NewFromString(test.expected)
|
expected, _ := NewFromString(test.expected)
|
||||||
got := d.Floor()
|
input, _ := NewFromString(test.input)
|
||||||
if !got.Equal(expected) {
|
assertFloor(input, expected)
|
||||||
t.Errorf("Floor(%s): got %s, expected %s", d, got, 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) {
|
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
|
input string
|
||||||
expected string
|
expected string
|
||||||
}
|
}
|
||||||
tests := []testData{
|
testsWithStrings := []testDataString{
|
||||||
{"1.999", "2"},
|
{"1.999", "2"},
|
||||||
{"1", "1"},
|
{"1", "1"},
|
||||||
{"1.01", "2"},
|
{"1.01", "2"},
|
||||||
|
@ -553,13 +603,35 @@ func TestDecimal_Ceil(t *testing.T) {
|
||||||
{"-1.01", "-1"},
|
{"-1.01", "-1"},
|
||||||
{"-1.999", "-1"},
|
{"-1.999", "-1"},
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range testsWithStrings {
|
||||||
d, _ := NewFromString(test.input)
|
|
||||||
expected, _ := NewFromString(test.expected)
|
expected, _ := NewFromString(test.expected)
|
||||||
got := d.Ceil()
|
input, _ := NewFromString(test.input)
|
||||||
if !got.Equal(expected) {
|
assertCeil(input, expected)
|
||||||
t.Errorf("Ceil(%s): got %s, expected %s", d, got, 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