diff --git a/fastfloat/parse.go b/fastfloat/parse.go index 5d4a7c7..b312cd9 100644 --- a/fastfloat/parse.go +++ b/fastfloat/parse.go @@ -213,6 +213,10 @@ func ParseBestEffort(s string) float64 { } } + // the integer part might be elided to remain compliant + // with https://go.dev/ref/spec#Floating-point_literals + intPartElided := s[i] == '.' + d := uint64(0) j := i for i < uint(len(s)) { @@ -232,7 +236,7 @@ func ParseBestEffort(s string) float64 { } break } - if i <= j { + if i <= j && !intPartElided { s = s[i:] if strings.HasPrefix(s, "+") { s = s[1:] @@ -263,7 +267,12 @@ func ParseBestEffort(s string) float64 { // Parse fractional part. i++ if i >= uint(len(s)) { - return 0 + if intPartElided { + return 0 + } + // the fractional part may be elided to remain compliant + // with https://go.dev/ref/spec#Floating-point_literals + return f } k := i for i < uint(len(s)) { @@ -296,6 +305,9 @@ func ParseBestEffort(s string) float64 { } } if s[i] == 'e' || s[i] == 'E' { + if intPartElided { + return 0 + } // Parse exponent part. i++ if i >= uint(len(s)) { @@ -363,6 +375,10 @@ func Parse(s string) (float64, error) { } } + // the integer part might be elided to remain compliant + // with https://go.dev/ref/spec#Floating-point_literals + intPartElided := s[i] == '.' + d := uint64(0) j := i for i < uint(len(s)) { @@ -382,7 +398,7 @@ func Parse(s string) (float64, error) { } break } - if i <= j { + if i <= j && !intPartElided { ss := s[i:] if strings.HasPrefix(ss, "+") { ss = ss[1:] @@ -413,7 +429,12 @@ func Parse(s string) (float64, error) { // Parse fractional part. i++ if i >= uint(len(s)) { - return 0, fmt.Errorf("cannot parse fractional part in %q", s) + if intPartElided { + return 0, fmt.Errorf("cannot parse integer or fractional part in %q", s) + } + // the fractional part might be elided to remain compliant + // with https://go.dev/ref/spec#Floating-point_literals + return f, nil } k := i for i < uint(len(s)) { @@ -446,6 +467,9 @@ func Parse(s string) (float64, error) { } } if s[i] == 'e' || s[i] == 'E' { + if intPartElided { + return 0, fmt.Errorf("cannot parse integer part in %q", s) + } // Parse exponent part. i++ if i >= uint(len(s)) { diff --git a/fastfloat/parse_test.go b/fastfloat/parse_test.go index c7930a7..7473c6f 100644 --- a/fastfloat/parse_test.go +++ b/fastfloat/parse_test.go @@ -206,6 +206,7 @@ func TestParseBestEffort(t *testing.T) { f("-", 0) f("--", 0) f("-.", 0) + f(".", 0) f("-.e", 0) f("+112", 0) f("++", 0) @@ -214,7 +215,6 @@ func TestParseBestEffort(t *testing.T) { f("-e12", 0) f(".", 0) f("..34", 0) - f("-.32", 0) f("-.e3", 0) f(".e+3", 0) @@ -224,7 +224,6 @@ func TestParseBestEffort(t *testing.T) { f("12.34.56", 0) f("13e34.56", 0) f("12.34e56e4", 0) - f("12.", 0) f("123..45", 0) f("123ee34", 0) f("123e", 0) @@ -262,6 +261,9 @@ func TestParseBestEffort(t *testing.T) { f("-0.1", -0.1) f("-0.123", -0.123) f("1.66", 1.66) + f("12.", 12) + f(".12", 0.12) + f("-.12", -0.12) f("12345.12345678901", 12345.12345678901) f("12345.123456789012", 12345.123456789012) f("12345.1234567890123", 12345.1234567890123) @@ -338,6 +340,7 @@ func TestParseFailure(t *testing.T) { f(" bar ") f("-") f("--") + f(".") f("-.") f("-.e") f("+112") @@ -347,7 +350,6 @@ func TestParseFailure(t *testing.T) { f("-e12") f(".") f("..34") - f("-.32") f("-.e3") f(".e+3") @@ -357,7 +359,6 @@ func TestParseFailure(t *testing.T) { f("12.34.56") f("13e34.56") f("12.34e56e4") - f("12.") f("123..45") f("123ee34") f("123e") @@ -413,6 +414,9 @@ func TestParseSuccess(t *testing.T) { f("-0.1", -0.1) f("-0.123", -0.123) f("1.66", 1.66) + f("12.", 12) + f(".12", 0.12) + f("-.12", -0.12) f("12345.12345678901", 12345.12345678901) f("12345.123456789012", 12345.123456789012) f("12345.1234567890123", 12345.1234567890123)