]> Cypherpunks repositories - gostls13.git/commitdiff
strconv: move all but Quote to internal/strconv
authorRuss Cox <rsc@golang.org>
Wed, 29 Oct 2025 01:41:40 +0000 (21:41 -0400)
committerRuss Cox <rsc@golang.org>
Wed, 29 Oct 2025 18:00:02 +0000 (11:00 -0700)
This will let low-level things depend on the canonical routines,
even for floating-point printing.

Change-Id: I31207dc6584ad90d4e365dbe6eaf20f8662ed22d
Reviewed-on: https://go-review.googlesource.com/c/go/+/716000
Reviewed-by: David Chase <drchase@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>

32 files changed:
src/go/build/deps_test.go
src/internal/strconv/atob.go [moved from src/strconv/atob.go with 95% similarity]
src/internal/strconv/atob_test.go [moved from src/strconv/atob_test.go with 72% similarity]
src/internal/strconv/atoc.go [moved from src/strconv/atoc.go with 80% similarity]
src/internal/strconv/atoc_test.go [moved from src/strconv/atoc_test.go with 88% similarity]
src/internal/strconv/atof.go [moved from src/strconv/atof.go with 96% similarity]
src/internal/strconv/atof_test.go [moved from src/strconv/atof_test.go with 98% similarity]
src/internal/strconv/atofeisel.go [moved from src/strconv/eisel_lemire.go with 95% similarity]
src/internal/strconv/atoi.go [moved from src/strconv/atoi.go with 69% similarity]
src/internal/strconv/atoi_test.go [moved from src/strconv/atoi_test.go with 82% similarity]
src/internal/strconv/ctoa.go [moved from src/strconv/ctoa.go with 100% similarity]
src/internal/strconv/ctoa_test.go [moved from src/strconv/ctoa_test.go with 98% similarity]
src/internal/strconv/decimal.go [moved from src/strconv/decimal.go with 100% similarity]
src/internal/strconv/decimal_test.go [moved from src/strconv/decimal_test.go with 99% similarity]
src/internal/strconv/deps.go [new file with mode: 0644]
src/internal/strconv/export_test.go [new file with mode: 0644]
src/internal/strconv/fp_test.go [moved from src/strconv/fp_test.go with 99% similarity]
src/internal/strconv/ftoa.go [moved from src/strconv/ftoa.go with 99% similarity]
src/internal/strconv/ftoa_test.go [moved from src/strconv/ftoa_test.go with 99% similarity]
src/internal/strconv/ftoaryu.go [moved from src/strconv/ftoaryu.go with 100% similarity]
src/internal/strconv/import_test.go [new file with mode: 0644]
src/internal/strconv/itoa.go [moved from src/strconv/itoa.go with 98% similarity]
src/internal/strconv/itoa_test.go [moved from src/strconv/itoa_test.go with 99% similarity]
src/internal/strconv/math.go [moved from src/strconv/math.go with 100% similarity]
src/internal/strconv/math_test.go [moved from src/strconv/math_test.go with 98% similarity]
src/internal/strconv/pow10gen.go [moved from src/strconv/pow10gen.go with 100% similarity]
src/internal/strconv/pow10tab.go [moved from src/strconv/pow10tab.go with 100% similarity]
src/internal/strconv/testdata/testfp.txt [moved from src/strconv/testdata/testfp.txt with 100% similarity]
src/strconv/export_test.go
src/strconv/import_test.go
src/strconv/number.go [new file with mode: 0644]
src/strconv/number_test.go [new file with mode: 0644]

index 853605f5c29df279760e8699b5e1e35588a0c2ec..10b27cfe927bac437e5705faba05edd29e78e03f 100644 (file)
@@ -69,6 +69,7 @@ var depsRules = `
 
        internal/goarch < internal/abi;
        internal/byteorder, internal/cpu, internal/goarch < internal/chacha8rand;
+       internal/goarch, math/bits < internal/strconv;
 
        # RUNTIME is the core runtime group of packages, all of them very light-weight.
        internal/abi,
@@ -80,6 +81,7 @@ var depsRules = `
        internal/goexperiment,
        internal/goos,
        internal/profilerecord,
+       internal/strconv,
        internal/trace/tracev2,
        math/bits,
        structs
similarity index 95%
rename from src/strconv/atob.go
rename to src/internal/strconv/atob.go
index 0a495008d7785e15544070114e4807ff21c3f42a..cbeba7f8bc940866115fa30da0b03a62e06310f8 100644 (file)
@@ -14,7 +14,7 @@ func ParseBool(str string) (bool, error) {
        case "0", "f", "F", "false", "FALSE", "False":
                return false, nil
        }
-       return false, syntaxError("ParseBool", str)
+       return false, ErrSyntax
 }
 
 // FormatBool returns "true" or "false" according to the value of b.
similarity index 72%
rename from src/strconv/atob_test.go
rename to src/internal/strconv/atob_test.go
index 40d43a9f8f9ba6970b08a372e5d1a8280ed710e4..61f543df3081a8d5b4be8a6c98ebf3338addccbd 100644 (file)
@@ -6,7 +6,7 @@ package strconv_test
 
 import (
        "bytes"
-       . "strconv"
+       . "internal/strconv"
        "testing"
 )
 
@@ -36,23 +36,8 @@ var atobtests = []atobTest{
 func TestParseBool(t *testing.T) {
        for _, test := range atobtests {
                b, e := ParseBool(test.in)
-               if test.err != nil {
-                       // expect an error
-                       if e == nil {
-                               t.Errorf("ParseBool(%s) = nil; want %s", test.in, test.err)
-                       } else {
-                               // NumError assertion must succeed; it's the only thing we return.
-                               if e.(*NumError).Err != test.err {
-                                       t.Errorf("ParseBool(%s) = %s; want %s", test.in, e, test.err)
-                               }
-                       }
-               } else {
-                       if e != nil {
-                               t.Errorf("ParseBool(%s) = %s; want nil", test.in, e)
-                       }
-                       if b != test.out {
-                               t.Errorf("ParseBool(%s) = %t; want %t", test.in, b, test.out)
-                       }
+               if b != test.out || e != test.err {
+                       t.Errorf("ParseBool(%s) = %v, %v, want %v, %v", test.in, b, e, test.out, test.err)
                }
        }
 }
similarity index 80%
rename from src/strconv/atoc.go
rename to src/internal/strconv/atoc.go
index 560bd7920df0905495bc74c15b592de184f5c1fe..52f2fc82af3bf181de968a140e1eb703aa419e4a 100644 (file)
@@ -4,23 +4,6 @@
 
 package strconv
 
-import "internal/stringslite"
-
-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 = stringslite.Clone(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
@@ -47,8 +30,6 @@ func ParseComplex(s string, bitSize int) (complex128, error) {
                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]
@@ -59,10 +40,10 @@ func ParseComplex(s string, bitSize int) (complex128, error) {
        // 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 {
+               if err != ErrRange {
                        return 0, err
                }
+               pending = err
        }
        s = s[n:]
 
@@ -88,20 +69,20 @@ func ParseComplex(s string, bitSize int) (complex128, error) {
                }
                fallthrough
        default:
-               return 0, syntaxError(fnParseComplex, orig)
+               return 0, ErrSyntax
        }
 
        // Read imaginary part.
        im, n, err := parseFloatPrefix(s, size)
        if err != nil {
-               err, pending = convErr(err, orig)
-               if err != nil {
+               if err != ErrRange {
                        return 0, err
                }
+               pending = err
        }
        s = s[n:]
        if s != "i" {
-               return 0, syntaxError(fnParseComplex, orig)
+               return 0, ErrSyntax
        }
        return complex(re, im), pending
 }
similarity index 88%
rename from src/strconv/atoc_test.go
rename to src/internal/strconv/atoc_test.go
index 4c1aad090001f69c86a77221ef5d3e6d3066a36e..0a7741a4ccf74ba244b74c1da6116370da306846 100644 (file)
@@ -7,8 +7,7 @@ package strconv_test
 import (
        "math"
        "math/cmplx"
-       "reflect"
-       . "strconv"
+       . "internal/strconv"
        "testing"
 )
 
@@ -188,30 +187,24 @@ func TestParseComplex(t *testing.T) {
        }
        for i := range tests {
                test := &tests[i]
-               if test.err != nil {
-                       test.err = &NumError{Func: "ParseComplex", Num: test.in, Err: test.err}
+               c, e := ParseComplex(test.in, 128)
+               if !sameComplex(c, test.out) || e != test.err {
+                       t.Errorf("ParseComplex(%s, 128) = %v, %v, want %v, %v", test.in, c, e, test.out, 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)
-               }
-
                if complex128(complex64(test.out)) == test.out {
-                       got, err := ParseComplex(test.in, 64)
-                       if !reflect.DeepEqual(err, test.err) {
-                               t.Fatalf("ParseComplex(%q, 64) = %v, %v; want %v, %v", test.in, got, err, test.out, test.err)
-                       }
-                       got64 := complex64(got)
-                       if complex128(got64) != test.out {
-                               t.Fatalf("ParseComplex(%q, 64) = %v, %v; want %v, %v", test.in, got, err, test.out, test.err)
+                       c, e := ParseComplex(test.in, 64)
+                       c64 := complex64(c)
+                       if !sameComplex(complex128(c64) , test.out) || e != test.err {
+                               t.Errorf("ParseComplex(%s, 64) = %v, %v, want %v, %v", test.in, c, e, test.out, test.err)
                        }
                }
        }
 }
 
+func sameComplex(c1, c2 complex128) bool {
+       return cmplx.IsNaN(c1) && cmplx.IsNaN(c2) || c1 == c2
+}
+
 // Issue 42297: allow ParseComplex(s, not_32_or_64) for legacy reasons
 func TestParseComplexIncorrectBitSize(t *testing.T) {
        const s = "1.5e308+1.0e307i"
similarity index 96%
rename from src/strconv/atof.go
rename to src/internal/strconv/atof.go
index c05d29825714519601c3e985614165d2714bf656..ada45dc0aa92c14fcdb2a5f0827da7f49ea617ae 100644 (file)
@@ -10,8 +10,6 @@ package strconv
 //   2) Multiply/divide decimal by powers of two until in range [0.5, 1)
 //   3) Multiply by 2^precision and round to get mantissa.
 
-import "math"
-
 var optimize = true // set to false to force slow-path conversions for testing
 
 // commonPrefixLenIgnoreCase returns the length of the common
@@ -58,11 +56,11 @@ func special(s string) (f float64, n int, ok bool) {
                        n = 3
                }
                if n == 3 || n == 8 {
-                       return math.Inf(sign), nsign + n, true
+                       return inf(sign), nsign + n, true
                }
        case 'n', 'N':
                if commonPrefixLenIgnoreCase(s, "nan") == 3 {
-                       return math.NaN(), 3, true
+                       return nan(), 3, true
                }
        }
        return 0, 0, false
@@ -546,7 +544,7 @@ func atofHex(s string, flt *floatInfo, mantissa uint64, exp int, neg, trunc bool
        if exp > maxExp { // infinity and range error
                mantissa = 1 << flt.mantbits
                exp = maxExp + 1
-               err = rangeError(fnParseFloat, s)
+               err = ErrRange
        }
 
        bits := mantissa & (1<<flt.mantbits - 1)
@@ -555,9 +553,9 @@ func atofHex(s string, flt *floatInfo, mantissa uint64, exp int, neg, trunc bool
                bits |= 1 << flt.mantbits << flt.expbits
        }
        if flt == &float32info {
-               return float64(math.Float32frombits(uint32(bits))), err
+               return float64(float32frombits(uint32(bits))), err
        }
-       return math.Float64frombits(bits), err
+       return float64frombits(bits), err
 }
 
 const fnParseFloat = "ParseFloat"
@@ -569,7 +567,7 @@ func atof32(s string) (f float32, n int, err error) {
 
        mantissa, exp, neg, trunc, hex, n, ok := readFloat(s)
        if !ok {
-               return 0, n, syntaxError(fnParseFloat, s)
+               return 0, n, ErrSyntax
        }
 
        if hex {
@@ -603,12 +601,12 @@ func atof32(s string) (f float32, n int, err error) {
        // Slow fallback.
        var d decimal
        if !d.set(s[:n]) {
-               return 0, n, syntaxError(fnParseFloat, s)
+               return 0, n, ErrSyntax
        }
        b, ovf := d.floatBits(&float32info)
-       f = math.Float32frombits(uint32(b))
+       f = float32frombits(uint32(b))
        if ovf {
-               err = rangeError(fnParseFloat, s)
+               err = ErrRange
        }
        return f, n, err
 }
@@ -620,7 +618,7 @@ func atof64(s string) (f float64, n int, err error) {
 
        mantissa, exp, neg, trunc, hex, n, ok := readFloat(s)
        if !ok {
-               return 0, n, syntaxError(fnParseFloat, s)
+               return 0, n, ErrSyntax
        }
 
        if hex {
@@ -654,12 +652,12 @@ func atof64(s string) (f float64, n int, err error) {
        // Slow fallback.
        var d decimal
        if !d.set(s[:n]) {
-               return 0, n, syntaxError(fnParseFloat, s)
+               return 0, n, ErrSyntax
        }
        b, ovf := d.floatBits(&float64info)
-       f = math.Float64frombits(b)
+       f = float64frombits(b)
        if ovf {
-               err = rangeError(fnParseFloat, s)
+               err = ErrRange
        }
        return f, n, err
 }
@@ -693,8 +691,8 @@ func atof64(s string) (f float64, n int, err error) {
 // [floating-point literals]: https://go.dev/ref/spec#Floating-point_literals
 func ParseFloat(s string, bitSize int) (float64, error) {
        f, n, err := parseFloatPrefix(s, bitSize)
-       if n != len(s) && (err == nil || err.(*NumError).Err != ErrSyntax) {
-               return 0, syntaxError(fnParseFloat, s)
+       if n != len(s) {
+               return 0, ErrSyntax
        }
        return f, err
 }
similarity index 98%
rename from src/strconv/atof_test.go
rename to src/internal/strconv/atof_test.go
index 7b287b421945ca2212479ca51e58356291e9202d..dd0b4dd6effc419aa8fd4b780569e542e158bbb8 100644 (file)
@@ -8,7 +8,7 @@ import (
        "math"
        "math/rand"
        "reflect"
-       . "strconv"
+       . "internal/strconv"
        "strings"
        "sync"
        "testing"
@@ -446,21 +446,6 @@ func initAtof() {
 }
 
 func initAtofOnce() {
-       // The atof routines return NumErrors wrapping
-       // the error and the string. Convert the table above.
-       for i := range atoftests {
-               test := &atoftests[i]
-               if test.err != nil {
-                       test.err = &NumError{"ParseFloat", test.in, test.err}
-               }
-       }
-       for i := range atof32tests {
-               test := &atof32tests[i]
-               if test.err != nil {
-                       test.err = &NumError{"ParseFloat", test.in, test.err}
-               }
-       }
-
        // Generate random inputs for tests and benchmarks
        if testing.Short() {
                atofRandomTests = make([]atofSimpleTest, 100)
@@ -497,7 +482,7 @@ func TestParseFloatPrefix(t *testing.T) {
                // correctly as "inf" with suffix.
                for _, suffix := range []string{" ", "q", "+", "-", "<", "=", ">", "(", ")", "i", "init"} {
                        in := test.in + suffix
-                       _, n, err := ParseFloatPrefix(in, 64)
+                       _, n, err := parseFloatPrefix(in, 64)
                        if err != nil {
                                t.Errorf("ParseFloatPrefix(%q, 64): err = %v; want no error", in, err)
                        }
similarity index 95%
rename from src/strconv/eisel_lemire.go
rename to src/internal/strconv/atofeisel.go
index 7bc9c71a40516c0fda57c436bc47cade48ee48a2..10b8c96bba94e2f7a9d3b6e25e6eb89b6d7b7e82 100644 (file)
@@ -18,7 +18,6 @@ package strconv
 // https://github.com/nigeltao/parse-number-fxx-test-data/blob/5280dcfccf6d0b02a65ae282dad0b6d9de50e039/script/test-go-strconv.go
 
 import (
-       "math"
        "math/bits"
 )
 
@@ -29,7 +28,7 @@ func eiselLemire64(man uint64, exp10 int, neg bool) (f float64, ok bool) {
        // Exp10 Range.
        if man == 0 {
                if neg {
-                       f = math.Float64frombits(0x8000000000000000) // Negative zero.
+                       f = float64frombits(0x8000000000000000) // Negative zero.
                }
                return f, true
        }
@@ -88,7 +87,7 @@ func eiselLemire64(man uint64, exp10 int, neg bool) (f float64, ok bool) {
        if neg {
                retBits |= 0x8000000000000000
        }
-       return math.Float64frombits(retBits), true
+       return float64frombits(retBits), true
 }
 
 func eiselLemire32(man uint64, exp10 int, neg bool) (f float32, ok bool) {
@@ -104,7 +103,7 @@ func eiselLemire32(man uint64, exp10 int, neg bool) (f float32, ok bool) {
        // Exp10 Range.
        if man == 0 {
                if neg {
-                       f = math.Float32frombits(0x80000000) // Negative zero.
+                       f = float32frombits(0x80000000) // Negative zero.
                }
                return f, true
        }
@@ -163,5 +162,5 @@ func eiselLemire32(man uint64, exp10 int, neg bool) (f float32, ok bool) {
        if neg {
                retBits |= 0x80000000
        }
-       return math.Float32frombits(uint32(retBits)), true
+       return float32frombits(uint32(retBits)), true
 }
similarity index 69%
rename from src/strconv/atoi.go
rename to src/internal/strconv/atoi.go
index 83e931fe241ca5a8fd60760116344d2cd76793d1..5bc259e7e55e83d8906cb7790b23397fa099a46a 100644 (file)
@@ -4,11 +4,6 @@
 
 package strconv
 
-import (
-       "errors"
-       "internal/stringslite"
-)
-
 // lower(c) is a lower-case letter if and only if
 // c is either that lower-case letter or the equivalent upper-case letter.
 // Instead of writing c == 'x' || c == 'X' one can write lower(c) == 'x'.
@@ -17,47 +12,28 @@ func lower(c byte) byte {
        return c | ('x' - 'X')
 }
 
-// ErrRange indicates that a value is out of range for the target type.
-var ErrRange = errors.New("value out of range")
-
-// ErrSyntax indicates that a value does not have the right syntax for the target type.
-var ErrSyntax = errors.New("invalid syntax")
-
-// A NumError records a failed conversion.
-type NumError struct {
-       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.)
-}
-
-func (e *NumError) Error() string {
-       return "strconv." + e.Func + ": " + "parsing " + Quote(e.Num) + ": " + e.Err.Error()
-}
-
-func (e *NumError) Unwrap() error { return e.Err }
-
-// All ParseXXX functions allow the input string to escape to the error value.
-// This hurts strconv.ParseXXX(string(b)) calls where b is []byte since
-// the conversion from []byte must allocate a string on the heap.
-// If we assume errors are infrequent, then we can avoid escaping the input
-// back to the output by copying it first. This allows the compiler to call
-// strconv.ParseXXX without a heap allocation for most []byte to string
-// conversions, since it can now prove that the string cannot escape Parse.
-
-func syntaxError(fn, str string) *NumError {
-       return &NumError{fn, stringslite.Clone(str), ErrSyntax}
-}
-
-func rangeError(fn, str string) *NumError {
-       return &NumError{fn, stringslite.Clone(str), ErrRange}
-}
+type Error int
 
-func baseError(fn, str string, base int) *NumError {
-       return &NumError{fn, stringslite.Clone(str), errors.New("invalid base " + Itoa(base))}
-}
+const (
+       _ Error = iota
+       ErrRange
+       ErrSyntax
+       ErrBase
+       ErrBitSize
+)
 
-func bitSizeError(fn, str string, bitSize int) *NumError {
-       return &NumError{fn, stringslite.Clone(str), errors.New("invalid bit size " + Itoa(bitSize))}
+func (e Error) Error() string {
+       switch e {
+       case ErrRange:
+               return "value out of range"
+       case ErrSyntax:
+               return "invalid syntax"
+       case ErrBase:
+               return "invalid base"
+       case ErrBitSize:
+               return "invalid bit size"
+       }
+       return "unknown error"
 }
 
 const intSize = 32 << (^uint(0) >> 63)
@@ -74,7 +50,7 @@ func ParseUint(s string, base int, bitSize int) (uint64, error) {
        const fnParseUint = "ParseUint"
 
        if s == "" {
-               return 0, syntaxError(fnParseUint, s)
+               return 0, ErrSyntax
        }
 
        base0 := base == 0
@@ -105,13 +81,13 @@ func ParseUint(s string, base int, bitSize int) (uint64, error) {
                }
 
        default:
-               return 0, baseError(fnParseUint, s0, base)
+               return 0, ErrBase
        }
 
        if bitSize == 0 {
                bitSize = IntSize
        } else if bitSize < 0 || bitSize > 64 {
-               return 0, bitSizeError(fnParseUint, s0, bitSize)
+               return 0, ErrBitSize
        }
 
        // Cutoff is the smallest number such that cutoff*base > maxUint64.
@@ -141,29 +117,29 @@ func ParseUint(s string, base int, bitSize int) (uint64, error) {
                case 'a' <= lower(c) && lower(c) <= 'z':
                        d = lower(c) - 'a' + 10
                default:
-                       return 0, syntaxError(fnParseUint, s0)
+                       return 0, ErrSyntax
                }
 
                if d >= byte(base) {
-                       return 0, syntaxError(fnParseUint, s0)
+                       return 0, ErrSyntax
                }
 
                if n >= cutoff {
                        // n*base overflows
-                       return maxVal, rangeError(fnParseUint, s0)
+                       return maxVal, ErrRange
                }
                n *= uint64(base)
 
                n1 := n + uint64(d)
                if n1 < n || n1 > maxVal {
                        // n+d overflows
-                       return maxVal, rangeError(fnParseUint, s0)
+                       return maxVal, ErrRange
                }
                n = n1
        }
 
        if underscores && !underscoreOK(s0) {
-               return 0, syntaxError(fnParseUint, s0)
+               return 0, ErrSyntax
        }
 
        return n, nil
@@ -198,11 +174,10 @@ func ParseInt(s string, base int, bitSize int) (i int64, err error) {
        const fnParseInt = "ParseInt"
 
        if s == "" {
-               return 0, syntaxError(fnParseInt, s)
+               return 0, ErrSyntax
        }
 
        // Pick off leading sign.
-       s0 := s
        neg := false
        switch s[0] {
        case '+':
@@ -215,9 +190,7 @@ func ParseInt(s string, base int, bitSize int) (i int64, err error) {
        // Convert unsigned and check range.
        var un uint64
        un, err = ParseUint(s, base, bitSize)
-       if err != nil && err.(*NumError).Err != ErrRange {
-               err.(*NumError).Func = fnParseInt
-               err.(*NumError).Num = stringslite.Clone(s0)
+       if err != nil && err != ErrRange {
                return 0, err
        }
 
@@ -227,10 +200,10 @@ func ParseInt(s string, base int, bitSize int) (i int64, err error) {
 
        cutoff := uint64(1 << uint(bitSize-1))
        if !neg && un >= cutoff {
-               return int64(cutoff - 1), rangeError(fnParseInt, s0)
+               return int64(cutoff - 1), ErrRange
        }
        if neg && un > cutoff {
-               return -int64(cutoff), rangeError(fnParseInt, s0)
+               return -int64(cutoff), ErrRange
        }
        n := int64(un)
        if neg {
@@ -251,7 +224,7 @@ func Atoi(s string) (int, error) {
                if s[0] == '-' || s[0] == '+' {
                        s = s[1:]
                        if len(s) < 1 {
-                               return 0, syntaxError(fnAtoi, s0)
+                               return 0, ErrSyntax
                        }
                }
 
@@ -259,7 +232,7 @@ func Atoi(s string) (int, error) {
                for _, ch := range []byte(s) {
                        ch -= '0'
                        if ch > 9 {
-                               return 0, syntaxError(fnAtoi, s0)
+                               return 0, ErrSyntax
                        }
                        n = n*10 + int(ch)
                }
@@ -271,9 +244,6 @@ func Atoi(s string) (int, error) {
 
        // Slow path for invalid, big, or underscored integers.
        i64, err := ParseInt(s, 10, 0)
-       if nerr, ok := err.(*NumError); ok {
-               nerr.Func = fnAtoi
-       }
        return int(i64), err
 }
 
similarity index 82%
rename from src/strconv/atoi_test.go
rename to src/internal/strconv/atoi_test.go
index ada175200c7b56d811b45b73c727c6d0016dfda6..e8f123ea5e500b00bb8083fe0cfd0e7a287d7a9f 100644 (file)
@@ -5,10 +5,9 @@
 package strconv_test
 
 import (
-       "errors"
        "fmt"
        "reflect"
-       . "strconv"
+       . "internal/strconv"
        "testing"
 )
 
@@ -316,47 +315,6 @@ var numErrorTests = []numErrorTest{
        {"1\x00.2", `strconv.ParseFloat: parsing "1\x00.2": failed`},
 }
 
-func init() {
-       // The parse routines return NumErrors wrapping
-       // the error and the string. Convert the tables above.
-       for i := range parseUint64Tests {
-               test := &parseUint64Tests[i]
-               if test.err != nil {
-                       test.err = &NumError{"ParseUint", test.in, test.err}
-               }
-       }
-       for i := range parseUint64BaseTests {
-               test := &parseUint64BaseTests[i]
-               if test.err != nil {
-                       test.err = &NumError{"ParseUint", test.in, test.err}
-               }
-       }
-       for i := range parseInt64Tests {
-               test := &parseInt64Tests[i]
-               if test.err != nil {
-                       test.err = &NumError{"ParseInt", test.in, test.err}
-               }
-       }
-       for i := range parseInt64BaseTests {
-               test := &parseInt64BaseTests[i]
-               if test.err != nil {
-                       test.err = &NumError{"ParseInt", test.in, test.err}
-               }
-       }
-       for i := range parseUint32Tests {
-               test := &parseUint32Tests[i]
-               if test.err != nil {
-                       test.err = &NumError{"ParseUint", test.in, test.err}
-               }
-       }
-       for i := range parseInt32Tests {
-               test := &parseInt32Tests[i]
-               if test.err != nil {
-                       test.err = &NumError{"ParseInt", test.in, test.err}
-               }
-       }
-}
-
 func TestParseUint32(t *testing.T) {
        for i := range parseUint32Tests {
                test := &parseUint32Tests[i]
@@ -475,62 +433,40 @@ func TestAtoi(t *testing.T) {
                for i := range parseInt32Tests {
                        test := &parseInt32Tests[i]
                        out, err := Atoi(test.in)
-                       var testErr error
-                       if test.err != nil {
-                               testErr = &NumError{"Atoi", test.in, test.err.(*NumError).Err}
-                       }
-                       if int(test.out) != out || !reflect.DeepEqual(testErr, err) {
-                               t.Errorf("Atoi(%q) = %v, %v want %v, %v",
-                                       test.in, out, err, test.out, testErr)
+                       if out !=int(test.out) || err != test.err {
+                               t.Errorf("Atoi(%q) = %v, %v, want %v, %v", test.in, out, err, test.out, test.err)
                        }
                }
        case 64:
                for i := range parseInt64Tests {
                        test := &parseInt64Tests[i]
                        out, err := Atoi(test.in)
-                       var testErr error
-                       if test.err != nil {
-                               testErr = &NumError{"Atoi", test.in, test.err.(*NumError).Err}
-                       }
-                       if test.out != int64(out) || !reflect.DeepEqual(testErr, err) {
-                               t.Errorf("Atoi(%q) = %v, %v want %v, %v",
-                                       test.in, out, err, test.out, testErr)
+                       if int64(out) != test.out || err != test.err {
+                               t.Errorf("Atoi(%q) = %v, %v, want %v, %v", test.in, out, err, test.out, test.err)
                        }
                }
        }
 }
 
-func bitSizeErrStub(name string, bitSize int) error {
-       return bitSizeError(name, "0", bitSize)
-}
-
-func baseErrStub(name string, base int) error {
-       return baseError(name, "0", base)
-}
-
-func noErrStub(name string, arg int) error {
-       return nil
-}
-
 type parseErrorTest struct {
        arg     int
-       errStub func(name string, arg int) error
+       err error
 }
 
 var parseBitSizeTests = []parseErrorTest{
-       {-1, bitSizeErrStub},
-       {0, noErrStub},
-       {64, noErrStub},
-       {65, bitSizeErrStub},
+       {-1, ErrBitSize},
+       {0, nil},
+       {64, nil},
+       {65, ErrBitSize},
 }
 
 var parseBaseTests = []parseErrorTest{
-       {-1, baseErrStub},
-       {0, noErrStub},
-       {1, baseErrStub},
-       {2, noErrStub},
-       {36, noErrStub},
-       {37, baseErrStub},
+       {-1, ErrBase},
+       {0, nil},
+       {1, ErrBase},
+       {2, nil},
+       {36, nil},
+       {37, ErrBase},
 }
 
 func equalError(a, b error) bool {
@@ -546,11 +482,10 @@ func equalError(a, b error) bool {
 func TestParseIntBitSize(t *testing.T) {
        for i := range parseBitSizeTests {
                test := &parseBitSizeTests[i]
-               testErr := test.errStub("ParseInt", test.arg)
                _, err := ParseInt("0", 0, test.arg)
-               if !equalError(testErr, err) {
+               if err != test.err {
                        t.Errorf("ParseInt(\"0\", 0, %v) = 0, %v want 0, %v",
-                               test.arg, err, testErr)
+                               test.arg, err, test.err)
                }
        }
 }
@@ -558,11 +493,10 @@ func TestParseIntBitSize(t *testing.T) {
 func TestParseUintBitSize(t *testing.T) {
        for i := range parseBitSizeTests {
                test := &parseBitSizeTests[i]
-               testErr := test.errStub("ParseUint", test.arg)
                _, err := ParseUint("0", 0, test.arg)
-               if !equalError(testErr, err) {
+               if err != test.err {
                        t.Errorf("ParseUint(\"0\", 0, %v) = 0, %v want 0, %v",
-                               test.arg, err, testErr)
+                               test.arg, err, test.err)
                }
        }
 }
@@ -570,11 +504,10 @@ func TestParseUintBitSize(t *testing.T) {
 func TestParseIntBase(t *testing.T) {
        for i := range parseBaseTests {
                test := &parseBaseTests[i]
-               testErr := test.errStub("ParseInt", test.arg)
                _, err := ParseInt("0", test.arg, 0)
-               if !equalError(testErr, err) {
+               if err != test.err {
                        t.Errorf("ParseInt(\"0\", %v, 0) = 0, %v want 0, %v",
-                               test.arg, err, testErr)
+                               test.arg, err, test.err)
                }
        }
 }
@@ -582,35 +515,14 @@ func TestParseIntBase(t *testing.T) {
 func TestParseUintBase(t *testing.T) {
        for i := range parseBaseTests {
                test := &parseBaseTests[i]
-               testErr := test.errStub("ParseUint", test.arg)
                _, err := ParseUint("0", test.arg, 0)
-               if !equalError(testErr, err) {
+               if err != test.err {
                        t.Errorf("ParseUint(\"0\", %v, 0) = 0, %v want 0, %v",
-                               test.arg, err, testErr)
-               }
-       }
-}
-
-func TestNumError(t *testing.T) {
-       for _, test := range numErrorTests {
-               err := &NumError{
-                       Func: "ParseFloat",
-                       Num:  test.num,
-                       Err:  errors.New("failed"),
-               }
-               if got := err.Error(); got != test.want {
-                       t.Errorf(`(&NumError{"ParseFloat", %q, "failed"}).Error() = %v, want %v`, test.num, got, test.want)
+                               test.arg, err, test.err)
                }
        }
 }
 
-func TestNumErrorUnwrap(t *testing.T) {
-       err := &NumError{Err: ErrSyntax}
-       if !errors.Is(err, ErrSyntax) {
-               t.Error("errors.Is failed, wanted success")
-       }
-}
-
 func BenchmarkParseInt(b *testing.B) {
        b.Run("Pos", func(b *testing.B) {
                benchmarkParseInt(b, 1)
similarity index 98%
rename from src/strconv/ctoa_test.go
rename to src/internal/strconv/ctoa_test.go
index 8b77898ecccf5ae0dbbd03ea98ddb925358f5364..c24f30272a2c1e0f077dceb0c949d22d579409b8 100644 (file)
@@ -5,7 +5,7 @@
 package strconv_test
 
 import (
-       . "strconv"
+       . "internal/strconv"
        "testing"
 )
 
similarity index 99%
rename from src/strconv/decimal_test.go
rename to src/internal/strconv/decimal_test.go
index 13a127f5b2cfc9a1123a86d2711132ca69555891..a60096e1c8e5b91c7b2535fcf22694d403c3d0c5 100644 (file)
@@ -5,7 +5,7 @@
 package strconv_test
 
 import (
-       . "strconv"
+       . "internal/strconv"
        "testing"
 )
 
diff --git a/src/internal/strconv/deps.go b/src/internal/strconv/deps.go
new file mode 100644 (file)
index 0000000..0433113
--- /dev/null
@@ -0,0 +1,30 @@
+// Copyright 2025 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
+
+import "unsafe"
+
+// Implementations to avoid importing other dependencies.
+
+// package math
+
+func float64frombits(b uint64) float64 { return *(*float64)(unsafe.Pointer(&b)) }
+func float32frombits(b uint32) float32 { return *(*float32)(unsafe.Pointer(&b)) }
+func float64bits(f float64) uint64     { return *(*uint64)(unsafe.Pointer(&f)) }
+func float32bits(f float32) uint32     { return *(*uint32)(unsafe.Pointer(&f)) }
+
+func inf(sign int) float64 {
+       var v uint64
+       if sign >= 0 {
+               v = 0x7FF0000000000000
+       } else {
+               v = 0xFFF0000000000000
+       }
+       return float64frombits(v)
+}
+
+func isNaN(f float64) (is bool) { return f != f }
+
+func nan() float64 { return float64frombits(0x7FF8000000000001) }
diff --git a/src/internal/strconv/export_test.go b/src/internal/strconv/export_test.go
new file mode 100644 (file)
index 0000000..bea741e
--- /dev/null
@@ -0,0 +1,28 @@
+// Copyright 2017 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
+
+type Uint128 = uint128
+
+var (
+       MulLog10_2       = mulLog10_2
+       MulLog2_10       = mulLog2_10
+       ParseFloatPrefix = parseFloatPrefix
+       Pow10            = pow10
+       Umul128          = umul128
+       Umul192          = umul192
+)
+
+func NewDecimal(i uint64) *decimal {
+       d := new(decimal)
+       d.Assign(i)
+       return d
+}
+
+func SetOptimize(b bool) bool {
+       old := optimize
+       optimize = b
+       return old
+}
similarity index 99%
rename from src/strconv/fp_test.go
rename to src/internal/strconv/fp_test.go
index 376e8f591c6deeb2528dfc31ab93f440797d752c..042328c7d4e4c800fa4ee2d16e5013d4e6d8a544 100644 (file)
@@ -8,7 +8,7 @@ import (
        "bufio"
        _ "embed"
        "fmt"
-       "strconv"
+       "internal/strconv"
        "strings"
        "testing"
 )
similarity index 99%
rename from src/strconv/ftoa.go
rename to src/internal/strconv/ftoa.go
index 1d4bf770be0111a6d5c54e2d8cff59f59f93ab58..1aec5447ece8b8a789985cee762a4a74f57bae1d 100644 (file)
 
 package strconv
 
-import "math"
+const (
+       lowerhex = "0123456789abcdef"
+       upperhex = "0123456789ABCDEF"
+)
 
 type floatInfo struct {
        mantbits uint
@@ -71,10 +74,10 @@ func genericFtoa(dst []byte, val float64, fmt byte, prec, bitSize int) []byte {
        var flt *floatInfo
        switch bitSize {
        case 32:
-               bits = uint64(math.Float32bits(float32(val)))
+               bits = uint64(float32bits(float32(val)))
                flt = &float32info
        case 64:
-               bits = math.Float64bits(val)
+               bits = float64bits(val)
                flt = &float64info
        default:
                panic("strconv: illegal AppendFloat/FormatFloat bitSize")
similarity index 99%
rename from src/strconv/ftoa_test.go
rename to src/internal/strconv/ftoa_test.go
index 40faa433a64cf81709532fa93c12175ef4d1321f..14d1200ff26b487be8a5753f38de04ec3c686c5b 100644 (file)
@@ -7,7 +7,7 @@ package strconv_test
 import (
        "math"
        "math/rand"
-       . "strconv"
+       . "internal/strconv"
        "testing"
 )
 
diff --git a/src/internal/strconv/import_test.go b/src/internal/strconv/import_test.go
new file mode 100644 (file)
index 0000000..0cbc451
--- /dev/null
@@ -0,0 +1,18 @@
+// Copyright 2025 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 . "internal/strconv"
+
+type uint128 = Uint128
+
+var (
+       mulLog10_2       = MulLog10_2
+       mulLog2_10       = MulLog2_10
+       parseFloatPrefix = ParseFloatPrefix
+       pow10            = Pow10
+       umul128          = Umul128
+       umul192          = Umul192
+)
similarity index 98%
rename from src/strconv/itoa.go
rename to src/internal/strconv/itoa.go
index 7884e8e9878996bb8d516e1b33335cb1d9aeade2..d4ca478eb0abe9e06e3c5cebfa851e6109f9c9ca 100644 (file)
@@ -5,8 +5,8 @@
 package strconv
 
 import (
+       "internal/goarch"
        "math/bits"
-       "runtime"
 )
 
 // FormatUint returns the string representation of i in the given base,
@@ -200,7 +200,7 @@ func formatBase10(a []byte, u uint64) int {
                // or can split the uint64 into uint32-sized chunks.
                // On most systems, the uint32 math is faster, but not all.
                // The decision here is based on benchmarking.
-               itoaPure64 = host64bit && runtime.GOARCH != "amd64" && runtime.GOARCH != "arm64" && runtime.GOARCH != "s390x"
+               itoaPure64 = host64bit && goarch.GOARCH != "amd64" && goarch.GOARCH != "arm64" && goarch.GOARCH != "s390x"
 
                // 64-bit systems can all use 64-bit div and mod by a constant,
                // which the compiler rewrites to use 64x64→128-bit multiplies.
similarity index 99%
rename from src/strconv/itoa_test.go
rename to src/internal/strconv/itoa_test.go
index 9dbf812d4be2795f341c09086818605e16874d69..1629e45d48c4a020b054fde845ee661806cc2bd6 100644 (file)
@@ -6,7 +6,7 @@ package strconv_test
 
 import (
        "fmt"
-       . "strconv"
+       . "internal/strconv"
        "testing"
 )
 
similarity index 98%
rename from src/strconv/math_test.go
rename to src/internal/strconv/math_test.go
index 19b7ea6c751963ad5a2512f5c90513d5fca5511a..d4f881b5e74d2060e49a700e278c4803beaec78a 100644 (file)
@@ -6,7 +6,7 @@ package strconv_test
 
 import (
        "math"
-       . "strconv"
+       . "internal/strconv"
        "testing"
 )
 
index 3518bb233cec0cb31a14587d6eca650788c6d988..7a3c761e683763eea31f39b3c79bb9afe00bb9ea 100644 (file)
@@ -4,27 +4,7 @@
 
 package strconv
 
-type Uint128 = uint128
-
 var (
-       BaseError        = baseError
-       BitSizeError     = bitSizeError
-       MulLog10_2       = mulLog10_2
-       MulLog2_10       = mulLog2_10
-       ParseFloatPrefix = parseFloatPrefix
-       Pow10            = pow10
-       Umul128          = umul128
-       Umul192          = umul192
+       BaseError    = baseError
+       BitSizeError = bitSizeError
 )
-
-func NewDecimal(i uint64) *decimal {
-       d := new(decimal)
-       d.Assign(i)
-       return d
-}
-
-func SetOptimize(b bool) bool {
-       old := optimize
-       optimize = b
-       return old
-}
index 05d957a6238b6b1fee82aa3dc21c413d9e5061f0..b44678bc7c45080ffa5caea6c75a4fcdcf490cf3 100644 (file)
@@ -6,15 +6,7 @@ package strconv_test
 
 import . "strconv"
 
-type uint128 = Uint128
-
 var (
-       baseError        = BaseError
-       bitSizeError     = BitSizeError
-       mulLog10_2       = MulLog10_2
-       mulLog2_10       = MulLog2_10
-       parseFloatPrefix = ParseFloatPrefix
-       pow10            = Pow10
-       umul128          = Umul128
-       umul192          = Umul192
+       baseError    = BaseError
+       bitSizeError = BitSizeError
 )
diff --git a/src/strconv/number.go b/src/strconv/number.go
new file mode 100644 (file)
index 0000000..3fa625c
--- /dev/null
@@ -0,0 +1,286 @@
+// Copyright 2025 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
+
+import (
+       "errors"
+       "internal/strconv"
+       "internal/stringslite"
+)
+
+// IntSize is the size in bits of an int or uint value.
+const IntSize = strconv.IntSize
+
+// ParseBool returns the boolean value represented by the string.
+// It accepts 1, t, T, TRUE, true, True, 0, f, F, FALSE, false, False.
+// Any other value returns an error.
+func ParseBool(str string) (bool, error) {
+       x, err := strconv.ParseBool(str)
+       if err != nil {
+               return x, toError("ParseBool", str, 0, 0, err)
+       }
+       return x, nil
+}
+
+// FormatBool returns "true" or "false" according to the value of b.
+func FormatBool(b bool) string {
+       return strconv.FormatBool(b)
+}
+
+// AppendBool appends "true" or "false", according to the value of b,
+// to dst and returns the extended buffer.
+func AppendBool(dst []byte, b bool) []byte {
+       return strconv.AppendBool(dst, b)
+}
+
+// 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) {
+       x, err := strconv.ParseComplex(s, bitSize)
+       if err != nil {
+               return x, toError("ParseComplex", s, 0, bitSize, err)
+       }
+       return x, nil
+}
+
+// ParseFloat converts the string s to a floating-point number
+// with the precision specified by bitSize: 32 for float32, or 64 for float64.
+// When bitSize=32, the result still has type float64, but it will be
+// convertible to float32 without changing its value.
+//
+// ParseFloat accepts decimal and hexadecimal floating-point numbers
+// as defined by the Go syntax for [floating-point literals].
+// If s is well-formed and near a valid floating-point number,
+// ParseFloat returns the nearest floating-point number rounded
+// using IEEE754 unbiased rounding.
+// (Parsing a hexadecimal floating-point value only rounds when
+// there are more bits in the hexadecimal representation than
+// will fit in the mantissa.)
+//
+// The errors that ParseFloat returns have concrete type *NumError
+// and include err.Num = s.
+//
+// If s is not syntactically well-formed, ParseFloat returns err.Err = ErrSyntax.
+//
+// If s is syntactically well-formed but is more than 1/2 ULP
+// away from the largest floating point number of the given size,
+// ParseFloat returns f = Â±Inf, err.Err = ErrRange.
+//
+// ParseFloat recognizes the string "NaN", and the (possibly signed) strings "Inf" and "Infinity"
+// as their respective special floating point values. It ignores case when matching.
+//
+// [floating-point literals]: https://go.dev/ref/spec#Floating-point_literals
+func ParseFloat(s string, bitSize int) (float64, error) {
+       x, err := strconv.ParseFloat(s, bitSize)
+       if err != nil {
+               return x, toError("ParseFloat", s, 0, bitSize, err)
+       }
+       return x, nil
+}
+
+// ParseUint is like [ParseInt] but for unsigned numbers.
+//
+// A sign prefix is not permitted.
+func ParseUint(s string, base int, bitSize int) (uint64, error) {
+       x, err := strconv.ParseUint(s, base, bitSize)
+       if err != nil {
+               return x, toError("ParseUint", s, base, bitSize, err)
+       }
+       return x, nil
+}
+
+// ParseInt interprets a string s in the given base (0, 2 to 36) and
+// bit size (0 to 64) and returns the corresponding value i.
+//
+// The string may begin with a leading sign: "+" or "-".
+//
+// If the base argument is 0, the true base is implied by the string's
+// prefix following the sign (if present): 2 for "0b", 8 for "0" or "0o",
+// 16 for "0x", and 10 otherwise. Also, for argument base 0 only,
+// underscore characters are permitted as defined by the Go syntax for
+// [integer literals].
+//
+// The bitSize argument specifies the integer type
+// that the result must fit into. Bit sizes 0, 8, 16, 32, and 64
+// correspond to int, int8, int16, int32, and int64.
+// If bitSize is below 0 or above 64, an error is returned.
+//
+// The errors that ParseInt returns have concrete type [*NumError]
+// and include err.Num = s. If s is empty or contains invalid
+// digits, err.Err = [ErrSyntax] and the returned value is 0;
+// if the value corresponding to s cannot be represented by a
+// signed integer of the given size, err.Err = [ErrRange] and the
+// returned value is the maximum magnitude integer of the
+// appropriate bitSize and sign.
+//
+// [integer literals]: https://go.dev/ref/spec#Integer_literals
+func ParseInt(s string, base int, bitSize int) (i int64, err error) {
+       x, err := strconv.ParseInt(s, base, bitSize)
+       if err != nil {
+               return x, toError("ParseInt", s, base, bitSize, err)
+       }
+       return x, nil
+}
+
+// Atoi is equivalent to ParseInt(s, 10, 0), converted to type int.
+func Atoi(s string) (int, error) {
+       x, err := strconv.Atoi(s)
+       if err != nil {
+               return x, toError("Atoi", s, 0, 0, err)
+       }
+       return strconv.Atoi(s)
+}
+
+// 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 {
+       return strconv.FormatComplex(c, fmt, prec, bitSize)
+}
+
+// FormatFloat converts the floating-point number f to a string,
+// according to the format fmt and precision prec. It rounds the
+// result assuming that the original was obtained from a floating-point
+// value of bitSize bits (32 for float32, 64 for float64).
+//
+// The format fmt is one of
+//   - 'b' (-ddddp±ddd, a binary exponent),
+//   - 'e' (-d.dddde±dd, a decimal exponent),
+//   - 'E' (-d.ddddE±dd, a decimal exponent),
+//   - 'f' (-ddd.dddd, no exponent),
+//   - 'g' ('e' for large exponents, 'f' otherwise),
+//   - 'G' ('E' for large exponents, 'f' otherwise),
+//   - 'x' (-0xd.ddddp±ddd, a hexadecimal fraction and binary exponent), or
+//   - 'X' (-0Xd.ddddP±ddd, a hexadecimal fraction and binary exponent).
+//
+// The precision prec controls the number of digits (excluding the exponent)
+// printed by the 'e', 'E', 'f', 'g', 'G', 'x', and 'X' formats.
+// For 'e', 'E', 'f', 'x', and 'X', it is the number of digits after the decimal point.
+// For 'g' and 'G' it is the maximum number of significant digits (trailing
+// zeros are removed).
+// The special precision -1 uses the smallest number of digits
+// necessary such that ParseFloat will return f exactly.
+// The exponent is written as a decimal integer;
+// for all formats other than 'b', it will be at least two digits.
+func FormatFloat(f float64, fmt byte, prec, bitSize int) string {
+       return strconv.FormatFloat(f, fmt, prec, bitSize)
+}
+
+// AppendFloat appends the string form of the floating-point number f,
+// as generated by [FormatFloat], to dst and returns the extended buffer.
+func AppendFloat(dst []byte, f float64, fmt byte, prec, bitSize int) []byte {
+       return strconv.AppendFloat(dst, f, fmt, prec, bitSize)
+}
+
+// FormatUint returns the string representation of i in the given base,
+// for 2 <= base <= 36. The result uses the lower-case letters 'a' to 'z'
+// for digit values >= 10.
+func FormatUint(i uint64, base int) string {
+       return strconv.FormatUint(i, base)
+}
+
+// FormatInt returns the string representation of i in the given base,
+// for 2 <= base <= 36. The result uses the lower-case letters 'a' to 'z'
+// for digit values >= 10.
+func FormatInt(i int64, base int) string {
+       return strconv.FormatInt(i, base)
+}
+
+// Itoa is equivalent to [FormatInt](int64(i), 10).
+func Itoa(i int) string {
+       return strconv.Itoa(i)
+}
+
+// AppendInt appends the string form of the integer i,
+// as generated by [FormatInt], to dst and returns the extended buffer.
+func AppendInt(dst []byte, i int64, base int) []byte {
+       return strconv.AppendInt(dst, i, base)
+}
+
+// AppendUint appends the string form of the unsigned integer i,
+// as generated by [FormatUint], to dst and returns the extended buffer.
+func AppendUint(dst []byte, i uint64, base int) []byte {
+       return strconv.AppendUint(dst, i, base)
+}
+
+// toError converts from internal/strconv.Error to the error guaranteed by this package's APIs.
+func toError(fn, s string, base, bitSize int, err error) error {
+       switch err {
+       case strconv.ErrSyntax:
+               return syntaxError(fn, s)
+       case strconv.ErrRange:
+               return rangeError(fn, s)
+       case strconv.ErrBase:
+               return baseError(fn, s, base)
+       case strconv.ErrBitSize:
+               return bitSizeError(fn, s, bitSize)
+       }
+       return err
+}
+
+// ErrRange indicates that a value is out of range for the target type.
+var ErrRange = errors.New("value out of range")
+
+// ErrSyntax indicates that a value does not have the right syntax for the target type.
+var ErrSyntax = errors.New("invalid syntax")
+
+// A NumError records a failed conversion.
+type NumError struct {
+       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.)
+}
+
+func (e *NumError) Error() string {
+       return "strconv." + e.Func + ": " + "parsing " + Quote(e.Num) + ": " + e.Err.Error()
+}
+
+func (e *NumError) Unwrap() error { return e.Err }
+
+// All ParseXXX functions allow the input string to escape to the error value.
+// This hurts strconv.ParseXXX(string(b)) calls where b is []byte since
+// the conversion from []byte must allocate a string on the heap.
+// If we assume errors are infrequent, then we can avoid escaping the input
+// back to the output by copying it first. This allows the compiler to call
+// strconv.ParseXXX without a heap allocation for most []byte to string
+// conversions, since it can now prove that the string cannot escape Parse.
+
+func syntaxError(fn, str string) *NumError {
+       return &NumError{fn, stringslite.Clone(str), ErrSyntax}
+}
+
+func rangeError(fn, str string) *NumError {
+       return &NumError{fn, stringslite.Clone(str), ErrRange}
+}
+
+func baseError(fn, str string, base int) *NumError {
+       return &NumError{fn, stringslite.Clone(str), errors.New("invalid base " + Itoa(base))}
+}
+
+func bitSizeError(fn, str string, bitSize int) *NumError {
+       return &NumError{fn, stringslite.Clone(str), errors.New("invalid bit size " + Itoa(bitSize))}
+}
diff --git a/src/strconv/number_test.go b/src/strconv/number_test.go
new file mode 100644 (file)
index 0000000..4408bc0
--- /dev/null
@@ -0,0 +1,956 @@
+// Copyright 2009 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.
+
+// Note: These tests are focused mainly on generating the right errors.
+// The extensive numerical tests are in ../internal/strconv.
+// Add new tests there instead of here whenever possible.
+
+package strconv_test
+
+import (
+       "bytes"
+       "errors"
+       "math"
+       "math/cmplx"
+       "reflect"
+       . "strconv"
+       "testing"
+)
+
+type atobTest struct {
+       in  string
+       out bool
+       err error
+}
+
+var atobtests = []atobTest{
+       {"", false, ErrSyntax},
+       {"asdf", false, ErrSyntax},
+       {"0", false, nil},
+       {"false", false, nil},
+       {"true", true, nil},
+}
+
+func TestParseBool(t *testing.T) {
+       for _, test := range atobtests {
+               b, e := ParseBool(test.in)
+               if test.err != nil {
+                       // expect an error
+                       if e == nil {
+                               t.Errorf("ParseBool(%s) = nil; want %s", test.in, test.err)
+                       } else {
+                               // NumError assertion must succeed; it's the only thing we return.
+                               if e.(*NumError).Err != test.err {
+                                       t.Errorf("ParseBool(%s) = %s; want %s", test.in, e, test.err)
+                               }
+                       }
+               } else {
+                       if e != nil {
+                               t.Errorf("ParseBool(%s) = %s; want nil", test.in, e)
+                       }
+                       if b != test.out {
+                               t.Errorf("ParseBool(%s) = %t; want %t", test.in, b, test.out)
+                       }
+               }
+       }
+}
+
+var boolString = map[bool]string{
+       true:  "true",
+       false: "false",
+}
+
+func TestFormatBool(t *testing.T) {
+       for b, s := range boolString {
+               if f := FormatBool(b); f != s {
+                       t.Errorf("FormatBool(%v) = %q; want %q", b, f, s)
+               }
+       }
+}
+
+type appendBoolTest struct {
+       b   bool
+       in  []byte
+       out []byte
+}
+
+var appendBoolTests = []appendBoolTest{
+       {true, []byte("foo "), []byte("foo true")},
+       {false, []byte("foo "), []byte("foo false")},
+}
+
+func TestAppendBool(t *testing.T) {
+       for _, test := range appendBoolTests {
+               b := AppendBool(test.in, test.b)
+               if !bytes.Equal(b, test.out) {
+                       t.Errorf("AppendBool(%q, %v) = %q; want %q", test.in, test.b, b, test.out)
+               }
+       }
+}
+
+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)
+               }
+
+               if complex128(complex64(test.out)) == test.out {
+                       got, err := ParseComplex(test.in, 64)
+                       if !reflect.DeepEqual(err, test.err) {
+                               t.Fatalf("ParseComplex(%q, 64) = %v, %v; want %v, %v", test.in, got, err, test.out, test.err)
+                       }
+                       got64 := complex64(got)
+                       if complex128(got64) != test.out {
+                               t.Fatalf("ParseComplex(%q, 64) = %v, %v; want %v, %v", test.in, got, err, test.out, test.err)
+                       }
+               }
+       }
+}
+
+// Issue 42297: allow ParseComplex(s, not_32_or_64) for legacy reasons
+func TestParseComplexIncorrectBitSize(t *testing.T) {
+       const s = "1.5e308+1.0e307i"
+       const want = 1.5e308 + 1.0e307i
+
+       for _, bitSize := range []int{0, 10, 100, 256} {
+               c, err := ParseComplex(s, bitSize)
+               if err != nil {
+                       t.Fatalf("ParseComplex(%q, %d) gave error %s", s, bitSize, err)
+               }
+               if c != want {
+                       t.Fatalf("ParseComplex(%q, %d) = %g (expected %g)", s, bitSize, c, want)
+               }
+       }
+}
+
+type atofTest struct {
+       in  string
+       out string
+       err error
+}
+
+var atoftests = []atofTest{
+       {"", "0", ErrSyntax},
+       {"1.25", "1.25", nil},
+       {"+1", "1", nil},
+       {"1x", "0", ErrSyntax},
+       {"1.1.", "0", ErrSyntax},
+       {"1e23", "1e+23", nil},
+       {"1E23", "1e+23", nil},
+       {"0x1fFe2.p0", "131042", nil},
+       {"0x1fFe2.P0", "131042", nil},
+       {"-0x2p3", "-16", nil},
+       {"0x0.fp4", "15", nil},
+       {"0x0.fp0", "0.9375", nil},
+       {"0x1e2", "0", ErrSyntax},
+       {"1p2", "0", ErrSyntax},
+       {"0x1p1024", "+Inf", ErrRange},
+       {"-0x1p1024", "-Inf", ErrRange},
+       {"0x1.fffffffffffff7fffp1023", "1.7976931348623157e+308", nil},
+       {"-0x1.fffffffffffff7fffp1023", "-1.7976931348623157e+308", nil},
+       {"1.797693134862315808e308", "+Inf", ErrRange},
+       {"-1.797693134862315808e308", "-Inf", ErrRange},
+}
+
+func init() {
+       // The atof routines return NumErrors wrapping
+       // the error and the string. Convert the table above.
+       for i := range atoftests {
+               test := &atoftests[i]
+               if test.err != nil {
+                       test.err = &NumError{"ParseFloat", test.in, test.err}
+               }
+       }
+}
+
+func TestAtof(t *testing.T) {
+       for i := 0; i < len(atoftests); i++ {
+               test := &atoftests[i]
+               out, err := ParseFloat(test.in, 64)
+               outs := FormatFloat(out, 'g', -1, 64)
+               if outs != test.out || !reflect.DeepEqual(err, test.err) {
+                       t.Errorf("ParseFloat(%v, 64) = %v, %v want %v, %v",
+                               test.in, out, err, test.out, test.err)
+               }
+
+               if float64(float32(out)) == out {
+                       out, err := ParseFloat(test.in, 32)
+                       out32 := float32(out)
+                       if float64(out32) != out {
+                               t.Errorf("ParseFloat(%v, 32) = %v, not a float32 (closest is %v)", test.in, out, float64(out32))
+                               continue
+                       }
+                       outs := FormatFloat(float64(out32), 'g', -1, 32)
+                       if outs != test.out || !reflect.DeepEqual(err, test.err) {
+                               t.Errorf("ParseFloat(%v, 32) = %v, %v want %v, %v  # %v",
+                                       test.in, out32, err, test.out, test.err, out)
+                       }
+               }
+       }
+}
+
+type parseUint64Test struct {
+       in  string
+       out uint64
+       err error
+}
+
+var parseUint64Tests = []parseUint64Test{
+       {"", 0, ErrSyntax},
+       {"0", 0, nil},
+       {"1", 1, nil},
+       {"12345", 12345, nil},
+       {"012345", 12345, nil},
+       {"18446744073709551616", 1<<64 - 1, ErrRange},
+       {"-1", 0, ErrSyntax},
+}
+
+type parseUint64BaseTest struct {
+       in   string
+       base int
+       out  uint64
+       err  error
+}
+
+var parseUint64BaseTests = []parseUint64BaseTest{
+       {"", 0, 0, ErrSyntax},
+       {"0", 0, 0, nil},
+       {"1", 0, 1, nil},
+       {"-1", 0, 0, ErrSyntax},
+       {"12345", 0, 12345, nil},
+       {"012345", 0, 012345, nil},
+       {"18446744073709551616", 0, 1<<64 - 1, ErrRange},
+       {"0b", 0, 0, ErrSyntax},
+       {"101", 2, 5, nil},
+       {"101_", 2, 0, ErrSyntax},
+}
+
+type parseInt64Test struct {
+       in  string
+       out int64
+       err error
+}
+
+var parseInt64Tests = []parseInt64Test{
+       {"", 0, ErrSyntax},
+       {"0", 0, nil},
+       {"1", 1, nil},
+       {"-1", -1, nil},
+       {"12345", 12345, nil},
+       {"9223372036854775808", 1<<63 - 1, ErrRange},
+       {"123%45", 0, ErrSyntax},
+}
+
+type parseInt64BaseTest struct {
+       in   string
+       base int
+       out  int64
+       err  error
+}
+
+var parseInt64BaseTests = []parseInt64BaseTest{
+       {"", 0, 0, ErrSyntax},
+       {"0", 0, 0, nil},
+       {"1", 0, 1, nil},
+       {"-1", 0, -1, nil},
+       {"12345", 0, 12345, nil},
+       {"12345", 9, 8303, nil},
+       {"012345", 0, 012345, nil},
+       {"9223372036854775808", 10, 1<<63 - 1, ErrRange},
+       {"0b", 0, 0, ErrSyntax},
+       {"101", 2, 5, nil},
+       {"101_", 2, 0, ErrSyntax},
+}
+
+type parseUint32Test struct {
+       in  string
+       out uint32
+       err error
+}
+
+var parseUint32Tests = []parseUint32Test{
+       {"", 0, ErrSyntax},
+       {"0", 0, nil},
+       {"1", 1, nil},
+       {"12345", 12345, nil},
+       {"12345x", 0, ErrSyntax},
+       {"987654321", 987654321, nil},
+       {"4294967296", 1<<32 - 1, ErrRange},
+       {"1_2_3_4_5", 0, ErrSyntax}, // base=10 so no underscores allowed
+       {"12345_", 0, ErrSyntax},
+}
+
+type parseInt32Test struct {
+       in  string
+       out int32
+       err error
+}
+
+var parseInt32Tests = []parseInt32Test{
+       {"", 0, ErrSyntax},
+       {"0", 0, nil},
+       {"-0", 0, nil},
+       {"1", 1, nil},
+       {"-1", -1, nil},
+       {"12345", 12345, nil},
+       {"-12345", -12345, nil},
+       {"2147483648", 1<<31 - 1, ErrRange},
+       {"12345_", 0, ErrSyntax},
+}
+
+type numErrorTest struct {
+       num, want string
+}
+
+var numErrorTests = []numErrorTest{
+       {"0", `strconv.ParseFloat: parsing "0": failed`},
+       {"`", "strconv.ParseFloat: parsing \"`\": failed"},
+       {"1\x00.2", `strconv.ParseFloat: parsing "1\x00.2": failed`},
+}
+
+func init() {
+       // The parse routines return NumErrors wrapping
+       // the error and the string. Convert the tables above.
+       for i := range parseUint64Tests {
+               test := &parseUint64Tests[i]
+               if test.err != nil {
+                       test.err = &NumError{"ParseUint", test.in, test.err}
+               }
+       }
+       for i := range parseUint64BaseTests {
+               test := &parseUint64BaseTests[i]
+               if test.err != nil {
+                       test.err = &NumError{"ParseUint", test.in, test.err}
+               }
+       }
+       for i := range parseInt64Tests {
+               test := &parseInt64Tests[i]
+               if test.err != nil {
+                       test.err = &NumError{"ParseInt", test.in, test.err}
+               }
+       }
+       for i := range parseInt64BaseTests {
+               test := &parseInt64BaseTests[i]
+               if test.err != nil {
+                       test.err = &NumError{"ParseInt", test.in, test.err}
+               }
+       }
+       for i := range parseUint32Tests {
+               test := &parseUint32Tests[i]
+               if test.err != nil {
+                       test.err = &NumError{"ParseUint", test.in, test.err}
+               }
+       }
+       for i := range parseInt32Tests {
+               test := &parseInt32Tests[i]
+               if test.err != nil {
+                       test.err = &NumError{"ParseInt", test.in, test.err}
+               }
+       }
+}
+
+func TestParseUint32(t *testing.T) {
+       for i := range parseUint32Tests {
+               test := &parseUint32Tests[i]
+               out, err := ParseUint(test.in, 10, 32)
+               if uint64(test.out) != out || !reflect.DeepEqual(test.err, err) {
+                       t.Errorf("ParseUint(%q, 10, 32) = %v, %v want %v, %v",
+                               test.in, out, err, test.out, test.err)
+               }
+       }
+}
+
+func TestParseUint64(t *testing.T) {
+       for i := range parseUint64Tests {
+               test := &parseUint64Tests[i]
+               out, err := ParseUint(test.in, 10, 64)
+               if test.out != out || !reflect.DeepEqual(test.err, err) {
+                       t.Errorf("ParseUint(%q, 10, 64) = %v, %v want %v, %v",
+                               test.in, out, err, test.out, test.err)
+               }
+       }
+}
+
+func TestParseUint64Base(t *testing.T) {
+       for i := range parseUint64BaseTests {
+               test := &parseUint64BaseTests[i]
+               out, err := ParseUint(test.in, test.base, 64)
+               if test.out != out || !reflect.DeepEqual(test.err, err) {
+                       t.Errorf("ParseUint(%q, %v, 64) = %v, %v want %v, %v",
+                               test.in, test.base, out, err, test.out, test.err)
+               }
+       }
+}
+
+func TestParseInt32(t *testing.T) {
+       for i := range parseInt32Tests {
+               test := &parseInt32Tests[i]
+               out, err := ParseInt(test.in, 10, 32)
+               if int64(test.out) != out || !reflect.DeepEqual(test.err, err) {
+                       t.Errorf("ParseInt(%q, 10 ,32) = %v, %v want %v, %v",
+                               test.in, out, err, test.out, test.err)
+               }
+       }
+}
+
+func TestParseInt64(t *testing.T) {
+       for i := range parseInt64Tests {
+               test := &parseInt64Tests[i]
+               out, err := ParseInt(test.in, 10, 64)
+               if test.out != out || !reflect.DeepEqual(test.err, err) {
+                       t.Errorf("ParseInt(%q, 10, 64) = %v, %v want %v, %v",
+                               test.in, out, err, test.out, test.err)
+               }
+       }
+}
+
+func TestParseInt64Base(t *testing.T) {
+       for i := range parseInt64BaseTests {
+               test := &parseInt64BaseTests[i]
+               out, err := ParseInt(test.in, test.base, 64)
+               if test.out != out || !reflect.DeepEqual(test.err, err) {
+                       t.Errorf("ParseInt(%q, %v, 64) = %v, %v want %v, %v",
+                               test.in, test.base, out, err, test.out, test.err)
+               }
+       }
+}
+
+func TestParseUint(t *testing.T) {
+       switch IntSize {
+       case 32:
+               for i := range parseUint32Tests {
+                       test := &parseUint32Tests[i]
+                       out, err := ParseUint(test.in, 10, 0)
+                       if uint64(test.out) != out || !reflect.DeepEqual(test.err, err) {
+                               t.Errorf("ParseUint(%q, 10, 0) = %v, %v want %v, %v",
+                                       test.in, out, err, test.out, test.err)
+                       }
+               }
+       case 64:
+               for i := range parseUint64Tests {
+                       test := &parseUint64Tests[i]
+                       out, err := ParseUint(test.in, 10, 0)
+                       if test.out != out || !reflect.DeepEqual(test.err, err) {
+                               t.Errorf("ParseUint(%q, 10, 0) = %v, %v want %v, %v",
+                                       test.in, out, err, test.out, test.err)
+                       }
+               }
+       }
+}
+
+func TestParseInt(t *testing.T) {
+       switch IntSize {
+       case 32:
+               for i := range parseInt32Tests {
+                       test := &parseInt32Tests[i]
+                       out, err := ParseInt(test.in, 10, 0)
+                       if int64(test.out) != out || !reflect.DeepEqual(test.err, err) {
+                               t.Errorf("ParseInt(%q, 10, 0) = %v, %v want %v, %v",
+                                       test.in, out, err, test.out, test.err)
+                       }
+               }
+       case 64:
+               for i := range parseInt64Tests {
+                       test := &parseInt64Tests[i]
+                       out, err := ParseInt(test.in, 10, 0)
+                       if test.out != out || !reflect.DeepEqual(test.err, err) {
+                               t.Errorf("ParseInt(%q, 10, 0) = %v, %v want %v, %v",
+                                       test.in, out, err, test.out, test.err)
+                       }
+               }
+       }
+}
+
+func TestAtoi(t *testing.T) {
+       switch IntSize {
+       case 32:
+               for i := range parseInt32Tests {
+                       test := &parseInt32Tests[i]
+                       out, err := Atoi(test.in)
+                       var testErr error
+                       if test.err != nil {
+                               testErr = &NumError{"Atoi", test.in, test.err.(*NumError).Err}
+                       }
+                       if int(test.out) != out || !reflect.DeepEqual(testErr, err) {
+                               t.Errorf("Atoi(%q) = %v, %v want %v, %v",
+                                       test.in, out, err, test.out, testErr)
+                       }
+               }
+       case 64:
+               for i := range parseInt64Tests {
+                       test := &parseInt64Tests[i]
+                       out, err := Atoi(test.in)
+                       var testErr error
+                       if test.err != nil {
+                               testErr = &NumError{"Atoi", test.in, test.err.(*NumError).Err}
+                       }
+                       if test.out != int64(out) || !reflect.DeepEqual(testErr, err) {
+                               t.Errorf("Atoi(%q) = %v, %v want %v, %v",
+                                       test.in, out, err, test.out, testErr)
+                       }
+               }
+       }
+}
+
+func bitSizeErrStub(name string, bitSize int) error {
+       return bitSizeError(name, "0", bitSize)
+}
+
+func baseErrStub(name string, base int) error {
+       return baseError(name, "0", base)
+}
+
+func noErrStub(name string, arg int) error {
+       return nil
+}
+
+type parseErrorTest struct {
+       arg     int
+       errStub func(name string, arg int) error
+}
+
+var parseBitSizeTests = []parseErrorTest{
+       {-1, bitSizeErrStub},
+       {0, noErrStub},
+       {64, noErrStub},
+       {65, bitSizeErrStub},
+}
+
+var parseBaseTests = []parseErrorTest{
+       {-1, baseErrStub},
+       {0, noErrStub},
+       {1, baseErrStub},
+       {2, noErrStub},
+       {36, noErrStub},
+       {37, baseErrStub},
+}
+
+func equalError(a, b error) bool {
+       if a == nil {
+               return b == nil
+       }
+       if b == nil {
+               return a == nil
+       }
+       return a.Error() == b.Error()
+}
+
+func TestParseIntBitSize(t *testing.T) {
+       for i := range parseBitSizeTests {
+               test := &parseBitSizeTests[i]
+               testErr := test.errStub("ParseInt", test.arg)
+               _, err := ParseInt("0", 0, test.arg)
+               if !equalError(testErr, err) {
+                       t.Errorf("ParseInt(\"0\", 0, %v) = 0, %v want 0, %v",
+                               test.arg, err, testErr)
+               }
+       }
+}
+
+func TestParseUintBitSize(t *testing.T) {
+       for i := range parseBitSizeTests {
+               test := &parseBitSizeTests[i]
+               testErr := test.errStub("ParseUint", test.arg)
+               _, err := ParseUint("0", 0, test.arg)
+               if !equalError(testErr, err) {
+                       t.Errorf("ParseUint(\"0\", 0, %v) = 0, %v want 0, %v",
+                               test.arg, err, testErr)
+               }
+       }
+}
+
+func TestParseIntBase(t *testing.T) {
+       for i := range parseBaseTests {
+               test := &parseBaseTests[i]
+               testErr := test.errStub("ParseInt", test.arg)
+               _, err := ParseInt("0", test.arg, 0)
+               if !equalError(testErr, err) {
+                       t.Errorf("ParseInt(\"0\", %v, 0) = 0, %v want 0, %v",
+                               test.arg, err, testErr)
+               }
+       }
+}
+
+func TestParseUintBase(t *testing.T) {
+       for i := range parseBaseTests {
+               test := &parseBaseTests[i]
+               testErr := test.errStub("ParseUint", test.arg)
+               _, err := ParseUint("0", test.arg, 0)
+               if !equalError(testErr, err) {
+                       t.Errorf("ParseUint(\"0\", %v, 0) = 0, %v want 0, %v",
+                               test.arg, err, testErr)
+               }
+       }
+}
+
+func TestNumError(t *testing.T) {
+       for _, test := range numErrorTests {
+               err := &NumError{
+                       Func: "ParseFloat",
+                       Num:  test.num,
+                       Err:  errors.New("failed"),
+               }
+               if got := err.Error(); got != test.want {
+                       t.Errorf(`(&NumError{"ParseFloat", %q, "failed"}).Error() = %v, want %v`, test.num, got, test.want)
+               }
+       }
+}
+
+func TestNumErrorUnwrap(t *testing.T) {
+       err := &NumError{Err: ErrSyntax}
+       if !errors.Is(err, ErrSyntax) {
+               t.Error("errors.Is failed, wanted success")
+       }
+}
+
+func TestFormatComplex(t *testing.T) {
+       tests := []struct {
+               c       complex128
+               fmt     byte
+               prec    int
+               bitSize int
+               out     string
+       }{
+               // a variety of signs
+               {1 + 2i, 'g', -1, 128, "(1+2i)"},
+               {3 - 4i, 'g', -1, 128, "(3-4i)"},
+               {-5 + 6i, 'g', -1, 128, "(-5+6i)"},
+               {-7 - 8i, 'g', -1, 128, "(-7-8i)"},
+
+               // test that fmt and prec are working
+               {3.14159 + 0.00123i, 'e', 3, 128, "(3.142e+00+1.230e-03i)"},
+               {3.14159 + 0.00123i, 'f', 3, 128, "(3.142+0.001i)"},
+               {3.14159 + 0.00123i, 'g', 3, 128, "(3.14+0.00123i)"},
+
+               // ensure bitSize rounding is working
+               {1.2345678901234567 + 9.876543210987654i, 'f', -1, 128, "(1.2345678901234567+9.876543210987654i)"},
+               {1.2345678901234567 + 9.876543210987654i, 'f', -1, 64, "(1.2345679+9.876543i)"},
+
+               // other cases are handled by FormatFloat tests
+       }
+       for _, test := range tests {
+               out := FormatComplex(test.c, test.fmt, test.prec, test.bitSize)
+               if out != test.out {
+                       t.Fatalf("FormatComplex(%v, %q, %d, %d) = %q; want %q",
+                               test.c, test.fmt, test.prec, test.bitSize, out, test.out)
+               }
+       }
+}
+
+func TestFormatComplexInvalidBitSize(t *testing.T) {
+       defer func() {
+               if r := recover(); r == nil {
+                       t.Fatalf("expected panic due to invalid bitSize")
+               }
+       }()
+       _ = FormatComplex(1+2i, 'g', -1, 100)
+}
+
+type itob64Test struct {
+       in   int64
+       base int
+       out  string
+}
+
+var itob64tests = []itob64Test{
+       {0, 10, "0"},
+       {1, 10, "1"},
+       {-1, 10, "-1"},
+       {12345678, 10, "12345678"},
+       {-1 << 63, 10, "-9223372036854775808"},
+       {16, 17, "g"},
+       {25, 25, "10"},
+       {(((((17*36+24)*36+21)*36+34)*36+12)*36+24)*36 + 32, 36, "holycow"},
+}
+
+func TestItoa(t *testing.T) {
+       for _, test := range itob64tests {
+               s := FormatInt(test.in, test.base)
+               if s != test.out {
+                       t.Errorf("FormatInt(%v, %v) = %v want %v",
+                               test.in, test.base, s, test.out)
+               }
+               x := AppendInt([]byte("abc"), test.in, test.base)
+               if string(x) != "abc"+test.out {
+                       t.Errorf("AppendInt(%q, %v, %v) = %q want %v",
+                               "abc", test.in, test.base, x, test.out)
+               }
+
+               if test.in >= 0 {
+                       s := FormatUint(uint64(test.in), test.base)
+                       if s != test.out {
+                               t.Errorf("FormatUint(%v, %v) = %v want %v",
+                                       test.in, test.base, s, test.out)
+                       }
+                       x := AppendUint(nil, uint64(test.in), test.base)
+                       if string(x) != test.out {
+                               t.Errorf("AppendUint(%q, %v, %v) = %q want %v",
+                                       "abc", uint64(test.in), test.base, x, test.out)
+                       }
+               }
+
+               if test.base == 10 && int64(int(test.in)) == test.in {
+                       s := Itoa(int(test.in))
+                       if s != test.out {
+                               t.Errorf("Itoa(%v) = %v want %v",
+                                       test.in, s, test.out)
+                       }
+               }
+       }
+
+       // Override when base is illegal
+       defer func() {
+               if r := recover(); r == nil {
+                       t.Fatalf("expected panic due to illegal base")
+               }
+       }()
+       FormatUint(12345678, 1)
+}
+
+type uitob64Test struct {
+       in   uint64
+       base int
+       out  string
+}
+
+var uitob64tests = []uitob64Test{
+       {1<<63 - 1, 10, "9223372036854775807"},
+       {1 << 63, 10, "9223372036854775808"},
+       {1<<63 + 1, 10, "9223372036854775809"},
+       {1<<64 - 2, 10, "18446744073709551614"},
+       {1<<64 - 1, 10, "18446744073709551615"},
+       {1<<64 - 1, 2, "1111111111111111111111111111111111111111111111111111111111111111"},
+}
+
+func TestUitoa(t *testing.T) {
+       for _, test := range uitob64tests {
+               s := FormatUint(test.in, test.base)
+               if s != test.out {
+                       t.Errorf("FormatUint(%v, %v) = %v want %v",
+                               test.in, test.base, s, test.out)
+               }
+               x := AppendUint([]byte("abc"), test.in, test.base)
+               if string(x) != "abc"+test.out {
+                       t.Errorf("AppendUint(%q, %v, %v) = %q want %v",
+                               "abc", test.in, test.base, x, test.out)
+               }
+
+       }
+}
+
+var varlenUints = []struct {
+       in  uint64
+       out string
+}{
+       {1, "1"},
+       {12, "12"},
+       {123, "123"},
+       {1234, "1234"},
+       {12345, "12345"},
+       {123456, "123456"},
+       {1234567, "1234567"},
+       {12345678, "12345678"},
+       {123456789, "123456789"},
+       {1234567890, "1234567890"},
+       {12345678901, "12345678901"},
+       {123456789012, "123456789012"},
+       {1234567890123, "1234567890123"},
+       {12345678901234, "12345678901234"},
+       {123456789012345, "123456789012345"},
+       {1234567890123456, "1234567890123456"},
+       {12345678901234567, "12345678901234567"},
+       {123456789012345678, "123456789012345678"},
+       {1234567890123456789, "1234567890123456789"},
+       {12345678901234567890, "12345678901234567890"},
+}
+
+func TestFormatUintVarlen(t *testing.T) {
+       for _, test := range varlenUints {
+               s := FormatUint(test.in, 10)
+               if s != test.out {
+                       t.Errorf("FormatUint(%v, 10) = %v want %v", test.in, s, test.out)
+               }
+       }
+}