diff --git a/decimal.go b/decimal.go index 801c1a04..acee4fae 100644 --- a/decimal.go +++ b/decimal.go @@ -583,6 +583,33 @@ func (d Decimal) Pow(d2 Decimal) Decimal { return temp.Mul(temp).Div(d) } +// IsInteger returns true when decimal can be represented as an integer value, otherwise, it returns false. +func (d Decimal) IsInteger() bool { + // The most typical case, all decimal with exponent higher or equal 0 can be represented as integer + if d.exp >= 0 { + return true + } + // When the exponent is negative we have to check every number after the decimal place + // If all of them are zeroes, we are sure that given decimal can be represented as an integer + var r big.Int + q := big.NewInt(0).Set(d.value) + for z := abs(d.exp); z > 0; z-- { + q.QuoRem(q, tenInt, &r) + if r.Cmp(zeroInt) != 0 { + return false + } + } + return true +} + +// Abs calculates absolute value of any int32. Used for calculating absolute value of decimal's exponent. +func abs(n int32) int32 { + if n < 0 { + return -n + } + return n +} + // Cmp compares the numbers represented by d and d2 and returns: // // -1 if d < d2 diff --git a/decimal_bench_test.go b/decimal_bench_test.go index d77c2757..b505eb08 100644 --- a/decimal_bench_test.go +++ b/decimal_bench_test.go @@ -173,3 +173,13 @@ func Benchmark_decimal_Decimal_Sub_same_precision(b *testing.B) { d1.Add(d2) } } + +func BenchmarkDecimal_IsInteger(b *testing.B) { + d := RequireFromString("12.000") + + b.ReportAllocs() + b.StartTimer() + for i := 0; i < b.N; i++ { + d.IsInteger() + } +} diff --git a/decimal_test.go b/decimal_test.go index 488d4055..254053e4 100644 --- a/decimal_test.go +++ b/decimal_test.go @@ -2076,6 +2076,34 @@ func TestNegativePow(t *testing.T) { } } +func TestDecimal_IsInteger(t *testing.T) { + for _, testCase := range []struct { + Dec string + IsInteger bool + }{ + {"0", true}, + {"0.0000", true}, + {"0.01", false}, + {"0.01010101010000", false}, + {"12.0", true}, + {"12.00000000000000", true}, + {"12.10000", false}, + {"9999.0000", true}, + {"99999999.000000000", true}, + {"-656323444.0000000000000", true}, + {"-32768.01234", false}, + {"-32768.0123423562623600000", false}, + } { + d, err := NewFromString(testCase.Dec) + if err != nil { + t.Fatal(err) + } + if d.IsInteger() != testCase.IsInteger { + t.Errorf("expect %t, got %t, for %s", testCase.IsInteger, d.IsInteger(), testCase.Dec) + } + } +} + func TestDecimal_Sign(t *testing.T) { if Zero.Sign() != 0 { t.Errorf("%q should have sign 0", Zero)