aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/strconv
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2020-07-27 22:27:54 -0700
committerIan Lance Taylor <iant@golang.org>2020-08-01 11:21:40 -0700
commitf75af8c1464e948b5e166cf5ab09ebf0d82fc253 (patch)
tree3ba3299859b504bdeb477727471216bd094a0191 /libgo/go/strconv
parent75a23e59031fe673fc3b2e60fd1fe5f4c70ecb85 (diff)
downloadgcc-f75af8c1464e948b5e166cf5ab09ebf0d82fc253.zip
gcc-f75af8c1464e948b5e166cf5ab09ebf0d82fc253.tar.gz
gcc-f75af8c1464e948b5e166cf5ab09ebf0d82fc253.tar.bz2
libgo: update to go1.15rc1
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/245157
Diffstat (limited to 'libgo/go/strconv')
-rw-r--r--libgo/go/strconv/atoc.go105
-rw-r--r--libgo/go/strconv/atoc_test.go202
-rw-r--r--libgo/go/strconv/atof.go156
-rw-r--r--libgo/go/strconv/atof_test.go22
-rw-r--r--libgo/go/strconv/atoi.go6
-rw-r--r--libgo/go/strconv/ctoa.go27
-rw-r--r--libgo/go/strconv/extfloat.go24
-rw-r--r--libgo/go/strconv/internal_test.go4
-rw-r--r--libgo/go/strconv/quote_test.go66
9 files changed, 505 insertions, 107 deletions
diff --git a/libgo/go/strconv/atoc.go b/libgo/go/strconv/atoc.go
new file mode 100644
index 0000000..55b7c23
--- /dev/null
+++ b/libgo/go/strconv/atoc.go
@@ -0,0 +1,105 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package strconv
+
+const fnParseComplex = "ParseComplex"
+
+// convErr splits an error returned by parseFloatPrefix
+// into a syntax or range error for ParseComplex.
+func convErr(err error, s string) (syntax, range_ error) {
+ if x, ok := err.(*NumError); ok {
+ x.Func = fnParseComplex
+ x.Num = s
+ if x.Err == ErrRange {
+ return nil, x
+ }
+ }
+ return err, nil
+}
+
+// ParseComplex converts the string s to a complex number
+// with the precision specified by bitSize: 64 for complex64, or 128 for complex128.
+// When bitSize=64, the result still has type complex128, but it will be
+// convertible to complex64 without changing its value.
+//
+// The number represented by s must be of the form N, Ni, or N±Ni, where N stands
+// for a floating-point number as recognized by ParseFloat, and i is the imaginary
+// component. If the second N is unsigned, a + sign is required between the two components
+// as indicated by the ±. If the second N is NaN, only a + sign is accepted.
+// The form may be parenthesized and cannot contain any spaces.
+// The resulting complex number consists of the two components converted by ParseFloat.
+//
+// The errors that ParseComplex returns have concrete type *NumError
+// and include err.Num = s.
+//
+// If s is not syntactically well-formed, ParseComplex returns err.Err = ErrSyntax.
+//
+// If s is syntactically well-formed but either component is more than 1/2 ULP
+// away from the largest floating point number of the given component's size,
+// ParseComplex returns err.Err = ErrRange and c = ±Inf for the respective component.
+func ParseComplex(s string, bitSize int) (complex128, error) {
+ size := 128
+ if bitSize == 64 {
+ size = 32 // complex64 uses float32 parts
+ }
+
+ orig := s
+
+ // Remove parentheses, if any.
+ if len(s) >= 2 && s[0] == '(' && s[len(s)-1] == ')' {
+ s = s[1 : len(s)-1]
+ }
+
+ var pending error // pending range error, or nil
+
+ // Read real part (possibly imaginary part if followed by 'i').
+ re, n, err := parseFloatPrefix(s, size)
+ if err != nil {
+ err, pending = convErr(err, orig)
+ if err != nil {
+ return 0, err
+ }
+ }
+ s = s[n:]
+
+ // If we have nothing left, we're done.
+ if len(s) == 0 {
+ return complex(re, 0), pending
+ }
+
+ // Otherwise, look at the next character.
+ switch s[0] {
+ case '+':
+ // Consume the '+' to avoid an error if we have "+NaNi", but
+ // do this only if we don't have a "++" (don't hide that error).
+ if len(s) > 1 && s[1] != '+' {
+ s = s[1:]
+ }
+ case '-':
+ // ok
+ case 'i':
+ // If 'i' is the last character, we only have an imaginary part.
+ if len(s) == 1 {
+ return complex(0, re), pending
+ }
+ fallthrough
+ default:
+ return 0, syntaxError(fnParseComplex, orig)
+ }
+
+ // Read imaginary part.
+ im, n, err := parseFloatPrefix(s, size)
+ if err != nil {
+ err, pending = convErr(err, orig)
+ if err != nil {
+ return 0, err
+ }
+ }
+ s = s[n:]
+ if s != "i" {
+ return 0, syntaxError(fnParseComplex, orig)
+ }
+ return complex(re, im), pending
+}
diff --git a/libgo/go/strconv/atoc_test.go b/libgo/go/strconv/atoc_test.go
new file mode 100644
index 0000000..3aa421d
--- /dev/null
+++ b/libgo/go/strconv/atoc_test.go
@@ -0,0 +1,202 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package strconv_test
+
+import (
+ "math"
+ "math/cmplx"
+ "reflect"
+ . "strconv"
+ "testing"
+)
+
+var (
+ infp0 = complex(math.Inf(+1), 0)
+ infm0 = complex(math.Inf(-1), 0)
+ inf0p = complex(0, math.Inf(+1))
+ inf0m = complex(0, math.Inf(-1))
+
+ infpp = complex(math.Inf(+1), math.Inf(+1))
+ infpm = complex(math.Inf(+1), math.Inf(-1))
+ infmp = complex(math.Inf(-1), math.Inf(+1))
+ infmm = complex(math.Inf(-1), math.Inf(-1))
+)
+
+type atocTest struct {
+ in string
+ out complex128
+ err error
+}
+
+func TestParseComplex(t *testing.T) {
+ tests := []atocTest{
+ // Clearly invalid
+ {"", 0, ErrSyntax},
+ {" ", 0, ErrSyntax},
+ {"(", 0, ErrSyntax},
+ {")", 0, ErrSyntax},
+ {"i", 0, ErrSyntax},
+ {"+i", 0, ErrSyntax},
+ {"-i", 0, ErrSyntax},
+ {"1I", 0, ErrSyntax},
+ {"10 + 5i", 0, ErrSyntax},
+ {"3+", 0, ErrSyntax},
+ {"3+5", 0, ErrSyntax},
+ {"3+5+5i", 0, ErrSyntax},
+
+ // Parentheses
+ {"()", 0, ErrSyntax},
+ {"(i)", 0, ErrSyntax},
+ {"(0)", 0, nil},
+ {"(1i)", 1i, nil},
+ {"(3.0+5.5i)", 3.0 + 5.5i, nil},
+ {"(1)+1i", 0, ErrSyntax},
+ {"(3.0+5.5i", 0, ErrSyntax},
+ {"3.0+5.5i)", 0, ErrSyntax},
+
+ // NaNs
+ {"NaN", complex(math.NaN(), 0), nil},
+ {"NANi", complex(0, math.NaN()), nil},
+ {"nan+nAni", complex(math.NaN(), math.NaN()), nil},
+ {"+NaN", 0, ErrSyntax},
+ {"-NaN", 0, ErrSyntax},
+ {"NaN-NaNi", 0, ErrSyntax},
+
+ // Infs
+ {"Inf", infp0, nil},
+ {"+inf", infp0, nil},
+ {"-inf", infm0, nil},
+ {"Infinity", infp0, nil},
+ {"+INFINITY", infp0, nil},
+ {"-infinity", infm0, nil},
+ {"+infi", inf0p, nil},
+ {"0-infinityi", inf0m, nil},
+ {"Inf+Infi", infpp, nil},
+ {"+Inf-Infi", infpm, nil},
+ {"-Infinity+Infi", infmp, nil},
+ {"inf-inf", 0, ErrSyntax},
+
+ // Zeros
+ {"0", 0, nil},
+ {"0i", 0, nil},
+ {"-0.0i", 0, nil},
+ {"0+0.0i", 0, nil},
+ {"0e+0i", 0, nil},
+ {"0e-0+0i", 0, nil},
+ {"-0.0-0.0i", 0, nil},
+ {"0e+012345", 0, nil},
+ {"0x0p+012345i", 0, nil},
+ {"0x0.00p-012345i", 0, nil},
+ {"+0e-0+0e-0i", 0, nil},
+ {"0e+0+0e+0i", 0, nil},
+ {"-0e+0-0e+0i", 0, nil},
+
+ // Regular non-zeroes
+ {"0.1", 0.1, nil},
+ {"0.1i", 0 + 0.1i, nil},
+ {"0.123", 0.123, nil},
+ {"0.123i", 0 + 0.123i, nil},
+ {"0.123+0.123i", 0.123 + 0.123i, nil},
+ {"99", 99, nil},
+ {"+99", 99, nil},
+ {"-99", -99, nil},
+ {"+1i", 1i, nil},
+ {"-1i", -1i, nil},
+ {"+3+1i", 3 + 1i, nil},
+ {"30+3i", 30 + 3i, nil},
+ {"+3e+3-3e+3i", 3e+3 - 3e+3i, nil},
+ {"+3e+3+3e+3i", 3e+3 + 3e+3i, nil},
+ {"+3e+3+3e+3i+", 0, ErrSyntax},
+
+ // Separators
+ {"0.1", 0.1, nil},
+ {"0.1i", 0 + 0.1i, nil},
+ {"0.1_2_3", 0.123, nil},
+ {"+0x_3p3i", 0x3p3i, nil},
+ {"0_0+0x_0p0i", 0, nil},
+ {"0x_10.3p-8+0x3p3i", 0x10.3p-8 + 0x3p3i, nil},
+ {"+0x_1_0.3p-8+0x_3_0p3i", 0x10.3p-8 + 0x30p3i, nil},
+ {"0x1_0.3p+8-0x_3p3i", 0x10.3p+8 - 0x3p3i, nil},
+
+ // Hexadecimals
+ {"0x10.3p-8+0x3p3i", 0x10.3p-8 + 0x3p3i, nil},
+ {"+0x10.3p-8+0x3p3i", 0x10.3p-8 + 0x3p3i, nil},
+ {"0x10.3p+8-0x3p3i", 0x10.3p+8 - 0x3p3i, nil},
+ {"0x1p0", 1, nil},
+ {"0x1p1", 2, nil},
+ {"0x1p-1", 0.5, nil},
+ {"0x1ep-1", 15, nil},
+ {"-0x1ep-1", -15, nil},
+ {"-0x2p3", -16, nil},
+ {"0x1e2", 0, ErrSyntax},
+ {"1p2", 0, ErrSyntax},
+ {"0x1e2i", 0, ErrSyntax},
+
+ // ErrRange
+ // next float64 - too large
+ {"+0x1p1024", infp0, ErrRange},
+ {"-0x1p1024", infm0, ErrRange},
+ {"+0x1p1024i", inf0p, ErrRange},
+ {"-0x1p1024i", inf0m, ErrRange},
+ {"+0x1p1024+0x1p1024i", infpp, ErrRange},
+ {"+0x1p1024-0x1p1024i", infpm, ErrRange},
+ {"-0x1p1024+0x1p1024i", infmp, ErrRange},
+ {"-0x1p1024-0x1p1024i", infmm, ErrRange},
+ // the border is ...158079
+ // borderline - okay
+ {"+0x1.fffffffffffff7fffp1023+0x1.fffffffffffff7fffp1023i", 1.7976931348623157e+308 + 1.7976931348623157e+308i, nil},
+ {"+0x1.fffffffffffff7fffp1023-0x1.fffffffffffff7fffp1023i", 1.7976931348623157e+308 - 1.7976931348623157e+308i, nil},
+ {"-0x1.fffffffffffff7fffp1023+0x1.fffffffffffff7fffp1023i", -1.7976931348623157e+308 + 1.7976931348623157e+308i, nil},
+ {"-0x1.fffffffffffff7fffp1023-0x1.fffffffffffff7fffp1023i", -1.7976931348623157e+308 - 1.7976931348623157e+308i, nil},
+ // borderline - too large
+ {"+0x1.fffffffffffff8p1023", infp0, ErrRange},
+ {"-0x1fffffffffffff.8p+971", infm0, ErrRange},
+ {"+0x1.fffffffffffff8p1023i", inf0p, ErrRange},
+ {"-0x1fffffffffffff.8p+971i", inf0m, ErrRange},
+ {"+0x1.fffffffffffff8p1023+0x1.fffffffffffff8p1023i", infpp, ErrRange},
+ {"+0x1.fffffffffffff8p1023-0x1.fffffffffffff8p1023i", infpm, ErrRange},
+ {"-0x1fffffffffffff.8p+971+0x1fffffffffffff.8p+971i", infmp, ErrRange},
+ {"-0x1fffffffffffff8p+967-0x1fffffffffffff8p+967i", infmm, ErrRange},
+ // a little too large
+ {"1e308+1e308i", 1e+308 + 1e+308i, nil},
+ {"2e308+2e308i", infpp, ErrRange},
+ {"1e309+1e309i", infpp, ErrRange},
+ {"0x1p1025+0x1p1025i", infpp, ErrRange},
+ {"2e308", infp0, ErrRange},
+ {"1e309", infp0, ErrRange},
+ {"0x1p1025", infp0, ErrRange},
+ {"2e308i", inf0p, ErrRange},
+ {"1e309i", inf0p, ErrRange},
+ {"0x1p1025i", inf0p, ErrRange},
+ // way too large
+ {"+1e310+1e310i", infpp, ErrRange},
+ {"+1e310-1e310i", infpm, ErrRange},
+ {"-1e310+1e310i", infmp, ErrRange},
+ {"-1e310-1e310i", infmm, ErrRange},
+ // under/overflow exponent
+ {"1e-4294967296", 0, nil},
+ {"1e-4294967296i", 0, nil},
+ {"1e-4294967296+1i", 1i, nil},
+ {"1+1e-4294967296i", 1, nil},
+ {"1e-4294967296+1e-4294967296i", 0, nil},
+ {"1e+4294967296", infp0, ErrRange},
+ {"1e+4294967296i", inf0p, ErrRange},
+ {"1e+4294967296+1e+4294967296i", infpp, ErrRange},
+ {"1e+4294967296-1e+4294967296i", infpm, ErrRange},
+ }
+ for i := range tests {
+ test := &tests[i]
+ if test.err != nil {
+ test.err = &NumError{Func: "ParseComplex", Num: test.in, Err: test.err}
+ }
+ got, err := ParseComplex(test.in, 128)
+ if !reflect.DeepEqual(err, test.err) {
+ t.Fatalf("ParseComplex(%q, 128) = %v, %v; want %v, %v", test.in, got, err, test.out, test.err)
+ }
+ if !(cmplx.IsNaN(test.out) && cmplx.IsNaN(got)) && got != test.out {
+ t.Fatalf("ParseComplex(%q, 128) = %v, %v; want %v, %v", test.in, got, err, test.out, test.err)
+ }
+ }
+}
diff --git a/libgo/go/strconv/atof.go b/libgo/go/strconv/atof.go
index 4dbe68b..1f1d5ae 100644
--- a/libgo/go/strconv/atof.go
+++ b/libgo/go/strconv/atof.go
@@ -15,51 +15,61 @@ import "runtime"
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")
+ // Anything longer than "inf" is ok, but if we
+ // don't have "infinity", only consume "inf".
+ if 3 < n && n < 8 {
+ n = 3
}
- case 'n', 'N':
- if equalIgnoreCase(s, "nan") {
- return math.NaN(), true
+ if n == 3 || n == 8 {
+ return math.Inf(sign), nsign + n, true
}
- case 'i', 'I':
- if equalIgnoreCase(s, "inf") || equalIgnoreCase(s, "infinity") {
- return math.Inf(1), true
+ case 'n', 'N':
+ if commonPrefixLenIgnoreCase(s, "nan") == 3 {
+ return math.NaN(), 3, true
}
}
- return
+ return 0, 0, false
}
func (b *decimal) set(s string) (ok bool) {
@@ -159,11 +169,11 @@ 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
@@ -194,6 +204,7 @@ func readFloat(s string) (mantissa uint64, exp int, neg, trunc, hex, ok bool) {
nd := 0
ndMant := 0
dp := 0
+loop:
for ; i < len(s); i++ {
switch c := s[i]; true {
case c == '_':
@@ -202,7 +213,7 @@ func readFloat(s string) (mantissa uint64, exp int, neg, trunc, hex, ok bool) {
case c == '.':
if sawdot {
- return
+ break loop
}
sawdot = true
dp = nd
@@ -286,15 +297,11 @@ func readFloat(s string) (mantissa uint64, exp int, neg, trunc, hex, ok bool) {
return
}
- if i != len(s) {
- return
- }
-
if mantissa != 0 {
exp = dp - ndMant
}
- if underscores && !underscoreOK(s) {
+ if underscores && !underscoreOK(s[:i]) {
return
}
@@ -560,26 +567,26 @@ func atofHex(s string, flt *floatInfo, mantissa uint64, exp int, neg, trunc bool
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.
@@ -590,42 +597,43 @@ func atof32(s string) (f float32, err error) {
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.
@@ -636,21 +644,21 @@ func atof64(s string) (f float64, err error) {
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
@@ -675,12 +683,20 @@ func atof64(s string) (f float64, err error) {
// away from the largest floating point number of the given size,
// ParseFloat returns f = ±Inf, err.Err = ErrRange.
//
-// ParseFloat recognizes the strings "NaN", "+Inf", and "-Inf" as their
-// respective special floating point values. It ignores case when matching.
+// ParseFloat recognizes the strings "NaN", and the (possibly signed) strings "Inf" and "Infinity"
+// 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)
}
diff --git a/libgo/go/strconv/atof_test.go b/libgo/go/strconv/atof_test.go
index abe6c64..545d989 100644
--- a/libgo/go/strconv/atof_test.go
+++ b/libgo/go/strconv/atof_test.go
@@ -479,6 +479,28 @@ func initAtofOnce() {
}
}
+func TestParseFloatPrefix(t *testing.T) {
+ for i := range atoftests {
+ test := &atoftests[i]
+ if test.err != nil {
+ continue
+ }
+ // Adding characters that do not extend a number should not invalidate it.
+ // Test a few. The "i" and "init" cases test that we accept "infi", "infinit"
+ // correctly as "inf" with suffix.
+ for _, suffix := range []string{" ", "q", "+", "-", "<", "=", ">", "(", ")", "i", "init"} {
+ in := test.in + suffix
+ _, n, err := ParseFloatPrefix(in, 64)
+ if err != nil {
+ t.Errorf("ParseFloatPrefix(%q, 64): err = %v; want no error", in, err)
+ }
+ if n != len(test.in) {
+ t.Errorf("ParseFloatPrefix(%q, 64): n = %d; want %d", in, n, len(test.in))
+ }
+ }
+ }
+}
+
func testAtof(t *testing.T, opt bool) {
initAtof()
oldopt := SetOptimize(opt)
diff --git a/libgo/go/strconv/atoi.go b/libgo/go/strconv/atoi.go
index a4a8a37..f6c4efa 100644
--- a/libgo/go/strconv/atoi.go
+++ b/libgo/go/strconv/atoi.go
@@ -22,7 +22,7 @@ var ErrSyntax = errors.New("invalid syntax")
// A NumError records a failed conversion.
type NumError struct {
- Func string // the failing function (ParseBool, ParseInt, ParseUint, ParseFloat)
+ Func string // the failing function (ParseBool, ParseInt, ParseUint, ParseFloat, ParseComplex)
Num string // the input
Err error // the reason the conversion failed (e.g. ErrRange, ErrSyntax, etc.)
}
@@ -96,7 +96,7 @@ func ParseUint(s string, base int, bitSize int) (uint64, error) {
}
if bitSize == 0 {
- bitSize = int(IntSize)
+ bitSize = IntSize
} else if bitSize < 0 || bitSize > 64 {
return 0, bitSizeError(fnParseUint, s0, bitSize)
}
@@ -203,7 +203,7 @@ func ParseInt(s string, base int, bitSize int) (i int64, err error) {
}
if bitSize == 0 {
- bitSize = int(IntSize)
+ bitSize = IntSize
}
cutoff := uint64(1 << uint(bitSize-1))
diff --git a/libgo/go/strconv/ctoa.go b/libgo/go/strconv/ctoa.go
new file mode 100644
index 0000000..c16a2e5
--- /dev/null
+++ b/libgo/go/strconv/ctoa.go
@@ -0,0 +1,27 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package strconv
+
+// FormatComplex converts the complex number c to a string of the
+// form (a+bi) where a and b are the real and imaginary parts,
+// formatted according to the format fmt and precision prec.
+//
+// The format fmt and precision prec have the same meaning as in FormatFloat.
+// It rounds the result assuming that the original was obtained from a complex
+// value of bitSize bits, which must be 64 for complex64 and 128 for complex128.
+func FormatComplex(c complex128, fmt byte, prec, bitSize int) string {
+ if bitSize != 64 && bitSize != 128 {
+ panic("invalid bitSize")
+ }
+ bitSize >>= 1 // complex64 uses float32 internally
+
+ // Check if imaginary part has a sign. If not, add one.
+ im := FormatFloat(imag(c), fmt, prec, bitSize)
+ if im[0] != '+' && im[0] != '-' {
+ im = "+" + im
+ }
+
+ return "(" + FormatFloat(real(c), fmt, prec, bitSize) + im + "i)"
+}
diff --git a/libgo/go/strconv/extfloat.go b/libgo/go/strconv/extfloat.go
index 2a2dd7a..793a34d 100644
--- a/libgo/go/strconv/extfloat.go
+++ b/libgo/go/strconv/extfloat.go
@@ -231,8 +231,30 @@ var uint64pow10 = [...]uint64{
// float32 depending on flt.
func (f *extFloat) AssignDecimal(mantissa uint64, exp10 int, neg bool, trunc bool, flt *floatInfo) (ok bool) {
const uint64digits = 19
+
+ // Errors (in the "numerical approximation" sense, not the "Go's error
+ // type" sense) in this function are measured as multiples of 1/8 of a ULP,
+ // so that "1/2 of a ULP" can be represented in integer arithmetic.
+ //
+ // The C++ double-conversion library also uses this 8x scaling factor:
+ // https://github.com/google/double-conversion/blob/f4cb2384/double-conversion/strtod.cc#L291
+ // but this Go implementation has a bug, where it forgets to scale other
+ // calculations (further below in this function) by the same number. The
+ // C++ implementation does not forget:
+ // https://github.com/google/double-conversion/blob/f4cb2384/double-conversion/strtod.cc#L366
+ //
+ // Scaling the "errors" in the "is mant_extra in the range (halfway ±
+ // errors)" check, but not scaling the other values, means that we return
+ // ok=false (and fall back to a slower atof code path) more often than we
+ // could. This affects performance but not correctness.
+ //
+ // Longer term, we could fix the forgot-to-scale bug (and look carefully
+ // for correctness regressions; https://codereview.appspot.com/5494068
+ // landed in 2011), or replace this atof algorithm with a faster one (e.g.
+ // Ryu). Shorter term, this comment will suffice.
const errorscale = 8
- errors := 0 // An upper bound for error, computed in errorscale*ulp.
+
+ errors := 0 // An upper bound for error, computed in ULP/errorscale.
if trunc {
// the decimal number was truncated.
errors += errorscale / 2
diff --git a/libgo/go/strconv/internal_test.go b/libgo/go/strconv/internal_test.go
index d0fa80e..bb4a418 100644
--- a/libgo/go/strconv/internal_test.go
+++ b/libgo/go/strconv/internal_test.go
@@ -17,3 +17,7 @@ func SetOptimize(b bool) bool {
optimize = b
return old
}
+
+func ParseFloatPrefix(s string, bitSize int) (float64, int, error) {
+ return parseFloatPrefix(s, bitSize)
+}
diff --git a/libgo/go/strconv/quote_test.go b/libgo/go/strconv/quote_test.go
index cdc9aaf..f1faf13 100644
--- a/libgo/go/strconv/quote_test.go
+++ b/libgo/go/strconv/quote_test.go
@@ -180,39 +180,39 @@ type canBackquoteTest struct {
var canbackquotetests = []canBackquoteTest{
{"`", false},
- {string(0), false},
- {string(1), false},
- {string(2), false},
- {string(3), false},
- {string(4), false},
- {string(5), false},
- {string(6), false},
- {string(7), false},
- {string(8), false},
- {string(9), true}, // \t
- {string(10), false},
- {string(11), false},
- {string(12), false},
- {string(13), false},
- {string(14), false},
- {string(15), false},
- {string(16), false},
- {string(17), false},
- {string(18), false},
- {string(19), false},
- {string(20), false},
- {string(21), false},
- {string(22), false},
- {string(23), false},
- {string(24), false},
- {string(25), false},
- {string(26), false},
- {string(27), false},
- {string(28), false},
- {string(29), false},
- {string(30), false},
- {string(31), false},
- {string(0x7F), false},
+ {string(rune(0)), false},
+ {string(rune(1)), false},
+ {string(rune(2)), false},
+ {string(rune(3)), false},
+ {string(rune(4)), false},
+ {string(rune(5)), false},
+ {string(rune(6)), false},
+ {string(rune(7)), false},
+ {string(rune(8)), false},
+ {string(rune(9)), true}, // \t
+ {string(rune(10)), false},
+ {string(rune(11)), false},
+ {string(rune(12)), false},
+ {string(rune(13)), false},
+ {string(rune(14)), false},
+ {string(rune(15)), false},
+ {string(rune(16)), false},
+ {string(rune(17)), false},
+ {string(rune(18)), false},
+ {string(rune(19)), false},
+ {string(rune(20)), false},
+ {string(rune(21)), false},
+ {string(rune(22)), false},
+ {string(rune(23)), false},
+ {string(rune(24)), false},
+ {string(rune(25)), false},
+ {string(rune(26)), false},
+ {string(rune(27)), false},
+ {string(rune(28)), false},
+ {string(rune(29)), false},
+ {string(rune(30)), false},
+ {string(rune(31)), false},
+ {string(rune(0x7F)), false},
{`' !"#$%&'()*+,-./:;<=>?@[\]^_{|}~`, true},
{`0123456789`, true},
{`ABCDEFGHIJKLMNOPQRSTUVWXYZ`, true},