Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

optimize NewFromString a bit #198

Merged
merged 2 commits into from Jan 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
44 changes: 33 additions & 11 deletions decimal.go
Expand Up @@ -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 {
Expand Down
41 changes: 41 additions & 0 deletions decimal_bench_test.go
@@ -1,6 +1,7 @@
package decimal

import (
"fmt"
"math"
"math/rand"
"sort"
Expand Down Expand Up @@ -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)
}
}
}
}