mirror of
https://github.com/golang/go
synced 2024-09-29 11:34:32 -06:00
strconv: Speed improvement to number parsing
Run underscore validation only if we have seen underscores. Some performance results on my laptop: name old time/op new time/op delta Atof64Decimal-12 30.5ns ± 0% 23.8ns ± 0% -22.02% (p=0.016 n=5+4) Atof64Float-12 39.0ns ± 0% 28.7ns ± 0% -26.39% (p=0.002 n=6+6) Atof64FloatExp-12 64.4ns ± 1% 54.4ns ± 1% -15.65% (p=0.002 n=6+6) Atof64Big-12 115ns ± 1% 87ns ± 1% -24.45% (p=0.002 n=6+6) Atof64RandomBits-12 187ns ±14% 156ns ±19% -16.46% (p=0.032 n=6+6) Atof64RandomFloats-12 126ns ± 0% 105ns ± 1% -16.65% (p=0.000 n=6+5) Atof32Decimal-12 32.0ns ± 1% 24.0ns ± 1% -24.97% (p=0.002 n=6+6) Atof32Float-12 37.1ns ± 1% 27.0ns ± 1% -27.42% (p=0.002 n=6+6) Atof32FloatExp-12 68.4ns ± 1% 54.2ns ± 1% -20.77% (p=0.002 n=6+6) Atof32Random-12 92.0ns ± 1% 77.4ns ± 0% -15.81% (p=0.000 n=6+5) ParseInt/Pos/7bit-12 19.4ns ± 1% 13.8ns ±10% -28.94% (p=0.002 n=6+6) ParseInt/Pos/26bit-12 29.1ns ± 1% 19.8ns ± 2% -31.92% (p=0.002 n=6+6) ParseInt/Pos/31bit-12 33.1ns ± 0% 22.3ns ± 3% -32.62% (p=0.004 n=5+6) ParseInt/Pos/56bit-12 47.8ns ± 1% 30.7ns ± 1% -35.78% (p=0.004 n=6+5) ParseInt/Pos/63bit-12 51.9ns ± 1% 33.4ns ± 2% -35.49% (p=0.002 n=6+6) ParseInt/Neg/7bit-12 18.5ns ± 4% 13.4ns ± 3% -27.88% (p=0.002 n=6+6) ParseInt/Neg/26bit-12 28.4ns ± 3% 19.7ns ± 3% -30.38% (p=0.002 n=6+6) ParseInt/Neg/31bit-12 31.9ns ± 1% 21.8ns ± 2% -31.56% (p=0.002 n=6+6) ParseInt/Neg/56bit-12 46.2ns ± 0% 30.6ns ± 1% -33.73% (p=0.004 n=5+6) ParseInt/Neg/63bit-12 50.2ns ± 1% 33.2ns ± 1% -33.96% (p=0.002 n=6+6) Fixes #33330 Change-Id: I119da66457c2fbaf6e88bb90cf56417a16df8f0e Reviewed-on: https://go-review.googlesource.com/c/go/+/187957 Run-TryBot: Robert Griesemer <gri@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
parent
22355d6cd2
commit
3d48ae355b
@ -84,7 +84,7 @@ func (b *decimal) set(s string) (ok bool) {
|
||||
for ; i < len(s); i++ {
|
||||
switch {
|
||||
case s[i] == '_':
|
||||
// underscoreOK already called
|
||||
// readFloat already checked underscores
|
||||
continue
|
||||
case s[i] == '.':
|
||||
if sawdot {
|
||||
@ -140,7 +140,7 @@ func (b *decimal) set(s string) (ok bool) {
|
||||
e := 0
|
||||
for ; i < len(s) && ('0' <= s[i] && s[i] <= '9' || s[i] == '_'); i++ {
|
||||
if s[i] == '_' {
|
||||
// underscoreOK already called
|
||||
// readFloat already checked underscores
|
||||
continue
|
||||
}
|
||||
if e < 10000 {
|
||||
@ -159,10 +159,11 @@ func (b *decimal) set(s string) (ok bool) {
|
||||
}
|
||||
|
||||
// readFloat reads a decimal mantissa and exponent from a float
|
||||
// string representation. It returns ok==false if the number could
|
||||
// not fit return types or is invalid.
|
||||
// string representation. It returns ok==false if the number
|
||||
// is invalid.
|
||||
func readFloat(s string) (mantissa uint64, exp int, neg, trunc, hex, ok bool) {
|
||||
i := 0
|
||||
underscores := false
|
||||
|
||||
// optional sign
|
||||
if i >= len(s) {
|
||||
@ -195,7 +196,7 @@ func readFloat(s string) (mantissa uint64, exp int, neg, trunc, hex, ok bool) {
|
||||
for ; i < len(s); i++ {
|
||||
switch c := s[i]; true {
|
||||
case c == '_':
|
||||
// underscoreOK already called
|
||||
underscores = true
|
||||
continue
|
||||
|
||||
case c == '.':
|
||||
@ -271,7 +272,7 @@ func readFloat(s string) (mantissa uint64, exp int, neg, trunc, hex, ok bool) {
|
||||
e := 0
|
||||
for ; i < len(s) && ('0' <= s[i] && s[i] <= '9' || s[i] == '_'); i++ {
|
||||
if s[i] == '_' {
|
||||
// underscoreOK already called
|
||||
underscores = true
|
||||
continue
|
||||
}
|
||||
if e < 10000 {
|
||||
@ -291,6 +292,11 @@ func readFloat(s string) (mantissa uint64, exp int, neg, trunc, hex, ok bool) {
|
||||
if mantissa != 0 {
|
||||
exp = dp - ndMant
|
||||
}
|
||||
|
||||
if underscores && !underscoreOK(s) {
|
||||
return
|
||||
}
|
||||
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
@ -554,12 +560,16 @@ func atof32(s string) (f float32, err error) {
|
||||
}
|
||||
|
||||
mantissa, exp, neg, trunc, hex, ok := readFloat(s)
|
||||
if hex && ok {
|
||||
if !ok {
|
||||
return 0, syntaxError(fnParseFloat, s)
|
||||
}
|
||||
|
||||
if hex {
|
||||
f, err := atofHex(s, &float32info, mantissa, exp, neg, trunc)
|
||||
return float32(f), err
|
||||
}
|
||||
|
||||
if optimize && ok {
|
||||
if optimize {
|
||||
// Try pure floating-point arithmetic conversion.
|
||||
if !trunc {
|
||||
if f, ok := atof32exact(mantissa, exp, neg); ok {
|
||||
@ -597,11 +607,15 @@ func atof64(s string) (f float64, err error) {
|
||||
}
|
||||
|
||||
mantissa, exp, neg, trunc, hex, ok := readFloat(s)
|
||||
if hex && ok {
|
||||
if !ok {
|
||||
return 0, syntaxError(fnParseFloat, s)
|
||||
}
|
||||
|
||||
if hex {
|
||||
return atofHex(s, &float64info, mantissa, exp, neg, trunc)
|
||||
}
|
||||
|
||||
if optimize && ok {
|
||||
if optimize {
|
||||
// Try pure floating-point arithmetic conversion.
|
||||
if !trunc {
|
||||
if f, ok := atof64exact(mantissa, exp, neg); ok {
|
||||
@ -658,9 +672,6 @@ func atof64(s string) (f float64, err error) {
|
||||
// ParseFloat recognizes the strings "NaN", "+Inf", and "-Inf" as their
|
||||
// respective special floating point values. It ignores case when matching.
|
||||
func ParseFloat(s string, bitSize int) (float64, error) {
|
||||
if !underscoreOK(s) {
|
||||
return 0, syntaxError(fnParseFloat, s)
|
||||
}
|
||||
if bitSize == 32 {
|
||||
f, err := atof32(s)
|
||||
return float64(f), err
|
||||
|
@ -58,7 +58,7 @@ const maxUint64 = 1<<64 - 1
|
||||
func ParseUint(s string, base int, bitSize int) (uint64, error) {
|
||||
const fnParseUint = "ParseUint"
|
||||
|
||||
if s == "" || !underscoreOK(s) {
|
||||
if s == "" {
|
||||
return 0, syntaxError(fnParseUint, s)
|
||||
}
|
||||
|
||||
@ -113,12 +113,13 @@ func ParseUint(s string, base int, bitSize int) (uint64, error) {
|
||||
|
||||
maxVal := uint64(1)<<uint(bitSize) - 1
|
||||
|
||||
underscores := false
|
||||
var n uint64
|
||||
for _, c := range []byte(s) {
|
||||
var d byte
|
||||
switch {
|
||||
case c == '_' && base0:
|
||||
// underscoreOK already called
|
||||
underscores = true
|
||||
continue
|
||||
case '0' <= c && c <= '9':
|
||||
d = c - '0'
|
||||
@ -146,6 +147,10 @@ func ParseUint(s string, base int, bitSize int) (uint64, error) {
|
||||
n = n1
|
||||
}
|
||||
|
||||
if underscores && !underscoreOK(s0) {
|
||||
return 0, syntaxError(fnParseUint, s0)
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user