mirror of
https://github.com/shopspring/decimal.git
synced 2024-11-22 12:30:49 +01:00
optimize NewFromString a bit (#198)
* optimize NewFromString a bit * add benchmark for large number Co-authored-by: Nicholas Nordeen <nnordeen@fanatics.com>
This commit is contained in:
parent
8bd41ac55d
commit
0cada0bdfe
2 changed files with 74 additions and 11 deletions
44
decimal.go
44
decimal.go
|
@ -147,23 +147,45 @@ func NewFromString(value string) (Decimal, error) {
|
|||
exp = expInt
|
||||
}
|
||||
|
||||
parts := strings.Split(value, ".")
|
||||
if len(parts) == 1 {
|
||||
pIndex := -1
|
||||
vLen := len(value)
|
||||
for i := 0; i < vLen; i++ {
|
||||
if value[i] == '.' {
|
||||
if pIndex > -1 {
|
||||
return Decimal{}, fmt.Errorf("can't convert %s to decimal: too many .s", value)
|
||||
}
|
||||
pIndex = i
|
||||
}
|
||||
}
|
||||
|
||||
if pIndex == -1 {
|
||||
// There is no decimal point, we can just parse the original string as
|
||||
// an int
|
||||
intString = value
|
||||
} else if len(parts) == 2 {
|
||||
intString = parts[0] + parts[1]
|
||||
expInt := -len(parts[1])
|
||||
exp += int64(expInt)
|
||||
} else {
|
||||
return Decimal{}, fmt.Errorf("can't convert %s to decimal: too many .s", value)
|
||||
if pIndex+1 < vLen {
|
||||
intString = value[:pIndex] + value[pIndex+1:]
|
||||
} else {
|
||||
intString = value[:pIndex]
|
||||
}
|
||||
expInt := -len(value[pIndex+1:])
|
||||
exp += int64(expInt)
|
||||
}
|
||||
|
||||
dValue := new(big.Int)
|
||||
_, ok := dValue.SetString(intString, 10)
|
||||
if !ok {
|
||||
return Decimal{}, fmt.Errorf("can't convert %s to decimal", value)
|
||||
var dValue *big.Int
|
||||
// strconv.ParseInt is faster than new(big.Int).SetString so this is just a shortcut for strings we know won't overflow
|
||||
if len(intString) <= 18 {
|
||||
parsed64, err := strconv.ParseInt(intString, 10, 64)
|
||||
if err != nil {
|
||||
return Decimal{}, fmt.Errorf("can't convert %s to decimal", value)
|
||||
}
|
||||
dValue = big.NewInt(parsed64)
|
||||
} else {
|
||||
dValue = new(big.Int)
|
||||
_, ok := dValue.SetString(intString, 10)
|
||||
if !ok {
|
||||
return Decimal{}, fmt.Errorf("can't convert %s to decimal", value)
|
||||
}
|
||||
}
|
||||
|
||||
if exp < math.MinInt32 || exp > math.MaxInt32 {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package decimal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
"sort"
|
||||
|
@ -183,3 +184,43 @@ func BenchmarkDecimal_IsInteger(b *testing.B) {
|
|||
d.IsInteger()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDecimal_NewFromString(b *testing.B) {
|
||||
count := 72
|
||||
prices := make([]string, 0, count)
|
||||
for i := 1; i <= count; i++ {
|
||||
prices = append(prices, fmt.Sprintf("%d.%d", i*100, i))
|
||||
}
|
||||
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for _, p := range prices {
|
||||
d, err := NewFromString(p)
|
||||
if err != nil {
|
||||
b.Log(d)
|
||||
b.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDecimal_NewFromString_large_number(b *testing.B) {
|
||||
count := 72
|
||||
prices := make([]string, 0, count)
|
||||
for i := 1; i <= count; i++ {
|
||||
prices = append(prices, "9323372036854775807.9223372036854775807")
|
||||
}
|
||||
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for _, p := range prices {
|
||||
d, err := NewFromString(p)
|
||||
if err != nil {
|
||||
b.Log(d)
|
||||
b.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue