diff options
author | Ian Lance Taylor <iant@golang.org> | 2020-07-27 22:27:54 -0700 |
---|---|---|
committer | Ian Lance Taylor <iant@golang.org> | 2020-08-01 11:21:40 -0700 |
commit | f75af8c1464e948b5e166cf5ab09ebf0d82fc253 (patch) | |
tree | 3ba3299859b504bdeb477727471216bd094a0191 /libgo/go/strconv | |
parent | 75a23e59031fe673fc3b2e60fd1fe5f4c70ecb85 (diff) | |
download | gcc-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.go | 105 | ||||
-rw-r--r-- | libgo/go/strconv/atoc_test.go | 202 | ||||
-rw-r--r-- | libgo/go/strconv/atof.go | 156 | ||||
-rw-r--r-- | libgo/go/strconv/atof_test.go | 22 | ||||
-rw-r--r-- | libgo/go/strconv/atoi.go | 6 | ||||
-rw-r--r-- | libgo/go/strconv/ctoa.go | 27 | ||||
-rw-r--r-- | libgo/go/strconv/extfloat.go | 24 | ||||
-rw-r--r-- | libgo/go/strconv/internal_test.go | 4 | ||||
-rw-r--r-- | libgo/go/strconv/quote_test.go | 66 |
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}, |