Compare commits

..

5 commits

Author SHA1 Message Date
Philip Dubé
ceada80758
Merge d538aec685 into 547861c49b 2024-04-03 21:15:58 +00:00
Philip Dubé
d538aec685 remove copy, don't need Abs(d.value)
9007199254740992 converts to float64
2024-04-03 21:15:48 +00:00
Philip Dubé
0a79029c95 feedback, fixes bug 2024-04-03 21:15:48 +00:00
Philip Dubé
afe2a66041 Optimize NumDigits
Dividing BitLen by math.Log2(10) is what math/big does underneath

Not including the Int64/Uint64 check makes this slightly slower than old method

Included 2 benchmarks, for 10 digit numbers & 100 digit numbers:

-- before
> go test -bench=NumDigit -run=NumDigit
goos: linux
goarch: amd64
pkg: github.com/shopspring/decimal
cpu: AMD Ryzen 7 7840U w/ Radeon  780M Graphics
BenchmarkDecimal_NumDigits10-16     	18317293	        63.87 ns/op
BenchmarkDecimal_NumDigits100-16    	 3645015	       329.6 ns/op

-- after
...
BenchmarkDecimal_NumDigits10-16     	143781325	         8.488 ns/op
BenchmarkDecimal_NumDigits100-16    	 5931247	       207.4 ns/op
2024-04-03 21:15:48 +00:00
Philip Dubé
547861c49b
Avoid reallocation of initial slice in MarshalBinary (GobEncode) (#355) 2024-04-03 22:12:19 +02:00

View file

@ -1229,26 +1229,27 @@ func (d Decimal) NumDigits() int {
return 1
}
if d.value.IsUint64() {
u64 := d.value.Uint64()
if u64 < (1 << 53) {
if u64 == 0 {
if d.value.IsInt64() {
i64 := d.value.Int64()
if i64 <= (1<<53) && i64 >= -(1<<53) {
if i64 == 0 {
return 1
}
return int(math.Log10(float64(u64))) + 1
}
} else if d.value.IsInt64() {
i64 := d.value.Int64()
if i64 > -(1 << 53) {
return int(math.Log10(float64(-i64))) + 1
return int(math.Log10(math.Abs(float64(i64)))) + 1
}
}
abs := new(big.Int).Abs(d.value)
// lg10 may be off by 1, need to verify
lg10 := int(float64(abs.BitLen()) / math.Log2(10))
check := big.NewInt(int64(lg10))
return lg10 + abs.Cmp(check.Exp(tenInt, check, nil))
estimatedNumDigits := int(float64(d.value.BitLen()) / math.Log2(10))
// estimatedNumDigits (lg10) may be off by 1, need to verify
digitsBigInt := big.NewInt(int64(estimatedNumDigits))
errorCorrectionUnit := digitsBigInt.Exp(tenInt, digitsBigInt, nil)
if d.value.CmpAbs(errorCorrectionUnit) >= 0 {
return estimatedNumDigits + 1
}
return estimatedNumDigits
}
// IsInteger returns true when decimal can be represented as an integer value, otherwise, it returns false.
@ -1801,19 +1802,18 @@ func (d *Decimal) UnmarshalBinary(data []byte) error {
// MarshalBinary implements the encoding.BinaryMarshaler interface.
func (d Decimal) MarshalBinary() (data []byte, err error) {
// Write the exponent first since it's a fixed size
v1 := make([]byte, 4)
binary.BigEndian.PutUint32(v1, uint32(d.exp))
// Add the value
var v2 []byte
if v2, err = d.value.GobEncode(); err != nil {
return
// exp is written first, but encode value first to know output size
var valueData []byte
if valueData, err = d.value.GobEncode(); err != nil {
return nil, err
}
// Write the exponent in front, since it's a fixed size
expData := make([]byte, 4, len(valueData)+4)
binary.BigEndian.PutUint32(expData, uint32(d.exp))
// Return the byte array
data = append(v1, v2...)
return
return append(expData, valueData...), nil
}
// Scan implements the sql.Scanner interface for database deserialization.