Skip to content

Commit

Permalink
fastfloat: add fuzzy test from #22 (comment)
Browse files Browse the repository at this point in the history
  • Loading branch information
valyala committed Feb 23, 2020
1 parent 87743e6 commit 1649ca5
Show file tree
Hide file tree
Showing 4 changed files with 26 additions and 6 deletions.
10 changes: 6 additions & 4 deletions fastfloat/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,10 @@ func ParseInt64BestEffort(s string) int64 {
}

// Exact powers of 10.
//
// This works faster than math.Pow10, since it avoids additional multiplication.
var float64pow10 = [...]float64{
1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18,
1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16,
}

// ParseBestEffort parses floating-point number s.
Expand Down Expand Up @@ -169,8 +171,8 @@ func ParseBestEffort(s string) float64 {
if s[i] >= '0' && s[i] <= '9' {
d = d*10 + uint64(s[i]-'0')
i++
if i-j > 18 {
// The mantissa may be out of range. Fall back to standard parsing.
if i-j >= uint(len(float64pow10)) {
// The mantissa is out of range. Fall back to standard parsing.
f, err := strconv.ParseFloat(s, 64)
if err != nil && !math.IsInf(f, 0) {
return 0
Expand All @@ -185,7 +187,7 @@ func ParseBestEffort(s string) float64 {
return 0
}
// Convert the entire mantissa to a float at once to avoid rounding errors.
f = float64(d) / float64pow10[int(i-k)]
f = float64(d) / float64pow10[i-k]
if i >= uint(len(s)) {
// Fast path - parsed fractional number.
if minus {
Expand Down
18 changes: 18 additions & 0 deletions fastfloat/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package fastfloat

import (
"math"
"math/rand"
"strconv"
"testing"
)

Expand Down Expand Up @@ -189,3 +191,19 @@ func TestParseBestEffort(t *testing.T) {
f("naN", math.NaN())
f("NaN", math.NaN())
}

func TestParseBestEffortFuzz(t *testing.T) {
r := rand.New(rand.NewSource(0))
for i := 0; i < 100000; i++ {
f := r.Float64()
s := strconv.FormatFloat(f, 'g', -1, 64)
numExpected, err := strconv.ParseFloat(s, 64)
if err != nil {
t.Fatalf("unexpected error when parsing %q: %s", s, err)
}
num := ParseBestEffort(s)
if num != numExpected {
t.Fatalf("unexpected number parsed from %q; got %g; want %g", s, num, numExpected)
}
}
}
2 changes: 1 addition & 1 deletion fastfloat/parse_timing_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func BenchmarkParseInt64BestEffort(b *testing.B) {
}

func BenchmarkParseBestEffort(b *testing.B) {
for _, s := range []string{"0", "12", "12345", "1234567890", "1234.45678", "1234e45", "12.34e-34", "12345.123456789012", "12345.1234567890123"} {
for _, s := range []string{"0", "12", "12345", "1234567890", "1234.45678", "1234e45", "12.34e-34", "12345.1234567890", "12345.12345678901"} {
b.Run(s, func(b *testing.B) {
benchmarkParseBestEffort(b, s)
})
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module github.com/valyala/fastjson

go 1.13
go 1.12

0 comments on commit 1649ca5

Please sign in to comment.