var optimize = true // set to false to force slow-path conversions for testing
-func equalIgnoreCase(s1, s2 string) bool {
- if len(s1) != len(s2) {
- return false
- }
- for i := 0; i < len(s1); i++ {
- c1 := s1[i]
- if 'A' <= c1 && c1 <= 'Z' {
- c1 += 'a' - 'A'
+// commonPrefixLenIgnoreCase returns the length of the common
+// prefix of s and prefix, with the character case of s ignored.
+// The prefix argument must be all lower-case.
+func commonPrefixLenIgnoreCase(s, prefix string) int {
+ n := len(prefix)
+ if n > len(s) {
+ n = len(s)
+ }
+ for i := 0; i < n; i++ {
+ c := s[i]
+ if 'A' <= c && c <= 'Z' {
+ c += 'a' - 'A'
}
- c2 := s2[i]
- if 'A' <= c2 && c2 <= 'Z' {
- c2 += 'a' - 'A'
- }
- if c1 != c2 {
- return false
+ if c != prefix[i] {
+ return i
}
}
- return true
+ return n
}
-func special(s string) (f float64, ok bool) {
+// special returns the floating-point value for the special,
+// possibly signed floating-point representations inf, infinity,
+// and NaN. The result is ok if a prefix of s contains one
+// of these representations and n is the length of that prefix.
+// The character case is ignored.
+func special(s string) (f float64, n int, ok bool) {
if len(s) == 0 {
- return
+ return 0, 0, false
}
+ sign := 1
+ nsign := 0
switch s[0] {
- default:
- return
- case '+':
- if equalIgnoreCase(s, "+inf") || equalIgnoreCase(s, "+infinity") {
- return math.Inf(1), true
+ case '+', '-':
+ if s[0] == '-' {
+ sign = -1
}
- case '-':
- if equalIgnoreCase(s, "-inf") || equalIgnoreCase(s, "-infinity") {
- return math.Inf(-1), true
+ nsign = 1
+ s = s[1:]
+ fallthrough
+ case 'i', 'I':
+ n := commonPrefixLenIgnoreCase(s, "infinity")
+ // both "inf" and "infinity" are ok
+ if n == 3 || n == 8 {
+ return math.Inf(sign), nsign + n, true
}
case 'n', 'N':
- if equalIgnoreCase(s, "nan") {
- return math.NaN(), true
- }
- case 'i', 'I':
- if equalIgnoreCase(s, "inf") || equalIgnoreCase(s, "infinity") {
- return math.Inf(1), true
+ if commonPrefixLenIgnoreCase(s, "nan") == 3 {
+ return math.NaN(), 3, true
}
}
- return
+ return 0, 0, false
}
func (b *decimal) set(s string) (ok bool) {
return
}
-// readFloat reads a decimal mantissa and exponent from a float
-// 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
+// readFloat reads a decimal or hexadecimal mantissa and exponent from a float
+// string representation in s; the number may be followed by other characters.
+// readFloat reports the number of bytes consumed (i), and whether the number
+// is valid (ok).
+func readFloat(s string) (mantissa uint64, exp int, neg, trunc, hex bool, i int, ok bool) {
underscores := false
// optional sign
nd := 0
ndMant := 0
dp := 0
+loop:
for ; i < len(s); i++ {
switch c := s[i]; true {
case c == '_':
case c == '.':
if sawdot {
- return
+ break loop
}
sawdot = true
dp = nd
return
}
- if i != len(s) {
- return
- }
-
if mantissa != 0 {
exp = dp - ndMant
}
const fnParseFloat = "ParseFloat"
-func atof32(s string) (f float32, err error) {
- if val, ok := special(s); ok {
- return float32(val), nil
+func atof32(s string) (f float32, n int, err error) {
+ if val, n, ok := special(s); ok {
+ return float32(val), n, nil
}
- mantissa, exp, neg, trunc, hex, ok := readFloat(s)
+ mantissa, exp, neg, trunc, hex, n, ok := readFloat(s)
if !ok {
- return 0, syntaxError(fnParseFloat, s)
+ return 0, n, syntaxError(fnParseFloat, s)
}
if hex {
- f, err := atofHex(s, &float32info, mantissa, exp, neg, trunc)
- return float32(f), err
+ f, err := atofHex(s[:n], &float32info, mantissa, exp, neg, trunc)
+ return float32(f), n, err
}
if optimize {
// Try pure floating-point arithmetic conversion.
if !trunc {
if f, ok := atof32exact(mantissa, exp, neg); ok {
- return f, nil
+ return f, n, nil
}
}
// Try another fast path.
if ovf {
err = rangeError(fnParseFloat, s)
}
- return f, err
+ return f, n, err
}
}
// Slow fallback.
var d decimal
- if !d.set(s) {
- return 0, syntaxError(fnParseFloat, s)
+ if !d.set(s[:n]) {
+ return 0, n, syntaxError(fnParseFloat, s)
}
b, ovf := d.floatBits(&float32info)
f = math.Float32frombits(uint32(b))
if ovf {
err = rangeError(fnParseFloat, s)
}
- return f, err
+ return f, n, err
}
-func atof64(s string) (f float64, err error) {
- if val, ok := special(s); ok {
- return val, nil
+func atof64(s string) (f float64, n int, err error) {
+ if val, n, ok := special(s); ok {
+ return val, n, nil
}
- mantissa, exp, neg, trunc, hex, ok := readFloat(s)
+ mantissa, exp, neg, trunc, hex, n, ok := readFloat(s)
if !ok {
- return 0, syntaxError(fnParseFloat, s)
+ return 0, n, syntaxError(fnParseFloat, s)
}
if hex {
- return atofHex(s, &float64info, mantissa, exp, neg, trunc)
+ f, err := atofHex(s[:n], &float64info, mantissa, exp, neg, trunc)
+ return f, n, err
}
if optimize {
// Try pure floating-point arithmetic conversion.
if !trunc {
if f, ok := atof64exact(mantissa, exp, neg); ok {
- return f, nil
+ return f, n, nil
}
}
// Try another fast path.
if ovf {
err = rangeError(fnParseFloat, s)
}
- return f, err
+ return f, n, err
}
}
// Slow fallback.
var d decimal
- if !d.set(s) {
- return 0, syntaxError(fnParseFloat, s)
+ if !d.set(s[:n]) {
+ return 0, n, syntaxError(fnParseFloat, s)
}
b, ovf := d.floatBits(&float64info)
f = math.Float64frombits(b)
if ovf {
err = rangeError(fnParseFloat, s)
}
- return f, err
+ return f, n, err
}
// ParseFloat converts the string s to a floating-point number
// 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) {
+ f, n, err := parseFloatPrefix(s, bitSize)
+ if err == nil && n != len(s) {
+ return 0, syntaxError(fnParseFloat, s)
+ }
+ return f, err
+}
+
+func parseFloatPrefix(s string, bitSize int) (float64, int, error) {
if bitSize == 32 {
- f, err := atof32(s)
- return float64(f), err
+ f, n, err := atof32(s)
+ return float64(f), n, err
}
return atof64(s)
}