mirror of
https://github.com/shopspring/decimal.git
synced 2024-11-22 20:40:48 +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
38
decimal.go
38
decimal.go
|
@ -147,24 +147,46 @@ func NewFromString(value string) (Decimal, error) {
|
||||||
exp = expInt
|
exp = expInt
|
||||||
}
|
}
|
||||||
|
|
||||||
parts := strings.Split(value, ".")
|
pIndex := -1
|
||||||
if len(parts) == 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
|
// There is no decimal point, we can just parse the original string as
|
||||||
// an int
|
// an int
|
||||||
intString = value
|
intString = value
|
||||||
} else if len(parts) == 2 {
|
|
||||||
intString = parts[0] + parts[1]
|
|
||||||
expInt := -len(parts[1])
|
|
||||||
exp += int64(expInt)
|
|
||||||
} else {
|
} 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)
|
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)
|
_, ok := dValue.SetString(intString, 10)
|
||||||
if !ok {
|
if !ok {
|
||||||
return Decimal{}, fmt.Errorf("can't convert %s to decimal", value)
|
return Decimal{}, fmt.Errorf("can't convert %s to decimal", value)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if exp < math.MinInt32 || exp > math.MaxInt32 {
|
if exp < math.MinInt32 || exp > math.MaxInt32 {
|
||||||
// NOTE(vadim): I doubt a string could realistically be this long
|
// NOTE(vadim): I doubt a string could realistically be this long
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package decimal
|
package decimal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"sort"
|
"sort"
|
||||||
|
@ -183,3 +184,43 @@ func BenchmarkDecimal_IsInteger(b *testing.B) {
|
||||||
d.IsInteger()
|
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