summaryrefslogtreecommitdiff
path: root/src/pkg/strconv
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/strconv')
-rw-r--r--src/pkg/strconv/atof.go303
-rw-r--r--src/pkg/strconv/atof_test.go129
-rw-r--r--src/pkg/strconv/atoi.go24
-rw-r--r--src/pkg/strconv/atoi_test.go24
-rw-r--r--src/pkg/strconv/decimal.go2
-rw-r--r--src/pkg/strconv/extfloat.go358
-rw-r--r--src/pkg/strconv/fp_test.go31
-rw-r--r--src/pkg/strconv/ftoa.go161
-rw-r--r--src/pkg/strconv/ftoa_test.go70
-rw-r--r--src/pkg/strconv/isprint.go125
-rw-r--r--src/pkg/strconv/itoa_test.go30
-rw-r--r--src/pkg/strconv/quote.go5
-rw-r--r--src/pkg/strconv/strconv_test.go52
-rw-r--r--src/pkg/strconv/testdata/testfp.txt (renamed from src/pkg/strconv/testfp.txt)0
14 files changed, 919 insertions, 395 deletions
diff --git a/src/pkg/strconv/atof.go b/src/pkg/strconv/atof.go
index cd3031b0e..b4fe97d12 100644
--- a/src/pkg/strconv/atof.go
+++ b/src/pkg/strconv/atof.go
@@ -37,17 +37,28 @@ func equalIgnoreCase(s1, s2 string) bool {
}
func special(s string) (f float64, ok bool) {
- switch {
- case equalIgnoreCase(s, "nan"):
- return math.NaN(), true
- case equalIgnoreCase(s, "-inf"),
- equalIgnoreCase(s, "-infinity"):
- return math.Inf(-1), true
- case equalIgnoreCase(s, "+inf"),
- equalIgnoreCase(s, "+infinity"),
- equalIgnoreCase(s, "inf"),
- equalIgnoreCase(s, "infinity"):
- return math.Inf(1), true
+ if len(s) == 0 {
+ return
+ }
+ switch s[0] {
+ default:
+ return
+ case '+':
+ if equalIgnoreCase(s, "+inf") || equalIgnoreCase(s, "+infinity") {
+ return math.Inf(1), true
+ }
+ case '-':
+ if equalIgnoreCase(s, "-inf") || equalIgnoreCase(s, "-infinity") {
+ return math.Inf(-1), true
+ }
+ case 'n', 'N':
+ if equalIgnoreCase(s, "nan") {
+ return math.NaN(), true
+ }
+ case 'i', 'I':
+ if equalIgnoreCase(s, "inf") || equalIgnoreCase(s, "infinity") {
+ return math.Inf(1), true
+ }
}
return
}
@@ -142,6 +153,105 @@ func (b *decimal) set(s string) (ok bool) {
return
}
+// readFloat reads a decimal mantissa and exponent from a float
+// string representation. It sets ok to false if the number could
+// not fit return types or is invalid.
+func readFloat(s string) (mantissa uint64, exp int, neg, trunc, ok bool) {
+ const uint64digits = 19
+ i := 0
+
+ // optional sign
+ if i >= len(s) {
+ return
+ }
+ switch {
+ case s[i] == '+':
+ i++
+ case s[i] == '-':
+ neg = true
+ i++
+ }
+
+ // digits
+ sawdot := false
+ sawdigits := false
+ nd := 0
+ ndMant := 0
+ dp := 0
+ for ; i < len(s); i++ {
+ switch c := s[i]; true {
+ case c == '.':
+ if sawdot {
+ return
+ }
+ sawdot = true
+ dp = nd
+ continue
+
+ case '0' <= c && c <= '9':
+ sawdigits = true
+ if c == '0' && nd == 0 { // ignore leading zeros
+ dp--
+ continue
+ }
+ nd++
+ if ndMant < uint64digits {
+ mantissa *= 10
+ mantissa += uint64(c - '0')
+ ndMant++
+ } else if s[i] != '0' {
+ trunc = true
+ }
+ continue
+ }
+ break
+ }
+ if !sawdigits {
+ return
+ }
+ if !sawdot {
+ dp = nd
+ }
+
+ // optional exponent moves decimal point.
+ // if we read a very large, very long number,
+ // just be sure to move the decimal point by
+ // a lot (say, 100000). it doesn't matter if it's
+ // not the exact number.
+ if i < len(s) && (s[i] == 'e' || s[i] == 'E') {
+ i++
+ if i >= len(s) {
+ return
+ }
+ esign := 1
+ if s[i] == '+' {
+ i++
+ } else if s[i] == '-' {
+ i++
+ esign = -1
+ }
+ if i >= len(s) || s[i] < '0' || s[i] > '9' {
+ return
+ }
+ e := 0
+ for ; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ {
+ if e < 10000 {
+ e = e*10 + int(s[i]) - '0'
+ }
+ }
+ dp += e * esign
+ }
+
+ if i != len(s) {
+ return
+ }
+
+ exp = dp - ndMant
+ ok = true
+ return
+
+}
+
// decimal power of ten to binary power of two.
var powtab = []int{1, 3, 6, 9, 13, 16, 19, 23, 26}
@@ -243,19 +353,6 @@ out:
return bits, overflow
}
-// Compute exact floating-point integer from d's digits.
-// Caller is responsible for avoiding overflow.
-func (d *decimal) atof64int() float64 {
- f := 0.0
- for i := 0; i < d.nd; i++ {
- f = f*10 + float64(d.d[i]-'0')
- }
- if d.neg {
- f = -f
- }
- return f
-}
-
func (d *decimal) atof32int() float32 {
f := float32(0)
for i := 0; i < d.nd; i++ {
@@ -267,18 +364,6 @@ func (d *decimal) atof32int() float32 {
return f
}
-// Reads a uint64 decimal mantissa, which might be truncated.
-func (d *decimal) atou64() (mant uint64, digits int) {
- const uint64digits = 19
- for i, c := range d.d[:d.nd] {
- if i == uint64digits {
- return mant, i
- }
- mant = 10*mant + uint64(c-'0')
- }
- return mant, d.nd
-}
-
// Exact powers of 10.
var float64pow10 = []float64{
1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
@@ -287,69 +372,74 @@ var float64pow10 = []float64{
}
var float32pow10 = []float32{1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10}
-// If possible to convert decimal d to 64-bit float f exactly,
+// If possible to convert decimal representation to 64-bit float f exactly,
// entirely in floating-point math, do so, avoiding the expense of decimalToFloatBits.
// Three common cases:
// value is exact integer
// value is exact integer * exact power of ten
// value is exact integer / exact power of ten
// These all produce potentially inexact but correctly rounded answers.
-func (d *decimal) atof64() (f float64, ok bool) {
- // Exact integers are <= 10^15.
- // Exact powers of ten are <= 10^22.
- if d.nd > 15 {
+func atof64exact(mantissa uint64, exp int, neg bool) (f float64, ok bool) {
+ if mantissa>>float64info.mantbits != 0 {
return
}
+ f = float64(mantissa)
+ if neg {
+ f = -f
+ }
switch {
- case d.dp == d.nd: // int
- f := d.atof64int()
+ case exp == 0:
+ // an integer.
return f, true
-
- case d.dp > d.nd && d.dp <= 15+22: // int * 10^k
- f := d.atof64int()
- k := d.dp - d.nd
+ // Exact integers are <= 10^15.
+ // Exact powers of ten are <= 10^22.
+ case exp > 0 && exp <= 15+22: // int * 10^k
// If exponent is big but number of digits is not,
// can move a few zeros into the integer part.
- if k > 22 {
- f *= float64pow10[k-22]
- k = 22
+ if exp > 22 {
+ f *= float64pow10[exp-22]
+ exp = 22
}
- return f * float64pow10[k], true
-
- case d.dp < d.nd && d.nd-d.dp <= 22: // int / 10^k
- f := d.atof64int()
- return f / float64pow10[d.nd-d.dp], true
+ if f > 1e15 || f < -1e15 {
+ // the exponent was really too large.
+ return
+ }
+ return f * float64pow10[exp], true
+ case exp < 0 && exp >= -22: // int / 10^k
+ return f / float64pow10[-exp], true
}
return
}
-// If possible to convert decimal d to 32-bit float f exactly,
+// If possible to compute mantissa*10^exp to 32-bit float f exactly,
// entirely in floating-point math, do so, avoiding the machinery above.
-func (d *decimal) atof32() (f float32, ok bool) {
- // Exact integers are <= 10^7.
- // Exact powers of ten are <= 10^10.
- if d.nd > 7 {
+func atof32exact(mantissa uint64, exp int, neg bool) (f float32, ok bool) {
+ if mantissa>>float32info.mantbits != 0 {
return
}
+ f = float32(mantissa)
+ if neg {
+ f = -f
+ }
switch {
- case d.dp == d.nd: // int
- f := d.atof32int()
+ case exp == 0:
return f, true
-
- case d.dp > d.nd && d.dp <= 7+10: // int * 10^k
- f := d.atof32int()
- k := d.dp - d.nd
+ // Exact integers are <= 10^7.
+ // Exact powers of ten are <= 10^10.
+ case exp > 0 && exp <= 7+10: // int * 10^k
// If exponent is big but number of digits is not,
// can move a few zeros into the integer part.
- if k > 10 {
- f *= float32pow10[k-10]
- k = 10
+ if exp > 10 {
+ f *= float32pow10[exp-10]
+ exp = 10
}
- return f * float32pow10[k], true
-
- case d.dp < d.nd && d.nd-d.dp <= 10: // int / 10^k
- f := d.atof32int()
- return f / float32pow10[d.nd-d.dp], true
+ if f > 1e7 || f < -1e7 {
+ // the exponent was really too large.
+ return
+ }
+ return f * float32pow10[exp], true
+ case exp < 0 && exp >= -10: // int / 10^k
+ return f / float32pow10[-exp], true
}
return
}
@@ -361,15 +451,32 @@ func atof32(s string) (f float32, err error) {
return float32(val), nil
}
+ if optimize {
+ // Parse mantissa and exponent.
+ mantissa, exp, neg, trunc, ok := readFloat(s)
+ if ok {
+ // Try pure floating-point arithmetic conversion.
+ if !trunc {
+ if f, ok := atof32exact(mantissa, exp, neg); ok {
+ return f, nil
+ }
+ }
+ // Try another fast path.
+ ext := new(extFloat)
+ if ok := ext.AssignDecimal(mantissa, exp, neg, trunc, &float32info); ok {
+ b, ovf := ext.floatBits(&float32info)
+ f = math.Float32frombits(uint32(b))
+ if ovf {
+ err = rangeError(fnParseFloat, s)
+ }
+ return f, err
+ }
+ }
+ }
var d decimal
if !d.set(s) {
return 0, syntaxError(fnParseFloat, s)
}
- if optimize {
- if f, ok := d.atof32(); ok {
- return f, nil
- }
- }
b, ovf := d.floatBits(&float32info)
f = math.Float32frombits(uint32(b))
if ovf {
@@ -383,26 +490,32 @@ func atof64(s string) (f float64, err error) {
return val, nil
}
- var d decimal
- if !d.set(s) {
- return 0, syntaxError(fnParseFloat, s)
- }
if optimize {
- if f, ok := d.atof64(); ok {
- return f, nil
- }
-
- // Try another fast path.
- ext := new(extFloat)
- if ok := ext.AssignDecimal(&d); ok {
- b, ovf := ext.floatBits()
- f = math.Float64frombits(b)
- if ovf {
- err = rangeError(fnParseFloat, s)
+ // Parse mantissa and exponent.
+ mantissa, exp, neg, trunc, ok := readFloat(s)
+ if ok {
+ // Try pure floating-point arithmetic conversion.
+ if !trunc {
+ if f, ok := atof64exact(mantissa, exp, neg); ok {
+ return f, nil
+ }
+ }
+ // Try another fast path.
+ ext := new(extFloat)
+ if ok := ext.AssignDecimal(mantissa, exp, neg, trunc, &float64info); ok {
+ b, ovf := ext.floatBits(&float64info)
+ f = math.Float64frombits(b)
+ if ovf {
+ err = rangeError(fnParseFloat, s)
+ }
+ return f, err
}
- return f, err
}
}
+ var d decimal
+ if !d.set(s) {
+ return 0, syntaxError(fnParseFloat, s)
+ }
b, ovf := d.floatBits(&float64info)
f = math.Float64frombits(b)
if ovf {
diff --git a/src/pkg/strconv/atof_test.go b/src/pkg/strconv/atof_test.go
index 599502382..ba4933218 100644
--- a/src/pkg/strconv/atof_test.go
+++ b/src/pkg/strconv/atof_test.go
@@ -110,6 +110,7 @@ var atoftests = []atofTest{
{"1e", "0", ErrSyntax},
{"1e-", "0", ErrSyntax},
{".e-1", "0", ErrSyntax},
+ {"1\x00.2", "0", ErrSyntax},
// http://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072012e-308/
{"2.2250738585072012e-308", "2.2250738585072014e-308", nil},
@@ -134,6 +135,54 @@ var atoftests = []atofTest{
{"1.00000000000000011102230246251565404236316680908203125" + strings.Repeat("0", 10000) + "1", "1.0000000000000002", nil},
}
+var atof32tests = []atofTest{
+ // Exactly halfway between 1 and the next float32.
+ // Round to even (down).
+ {"1.000000059604644775390625", "1", nil},
+ // Slightly lower.
+ {"1.000000059604644775390624", "1", nil},
+ // Slightly higher.
+ {"1.000000059604644775390626", "1.0000001", nil},
+ // Slightly higher, but you have to read all the way to the end.
+ {"1.000000059604644775390625" + strings.Repeat("0", 10000) + "1", "1.0000001", nil},
+
+ // largest float32: (1<<128) * (1 - 2^-24)
+ {"340282346638528859811704183484516925440", "3.4028235e+38", nil},
+ {"-340282346638528859811704183484516925440", "-3.4028235e+38", nil},
+ // next float32 - too large
+ {"3.4028236e38", "+Inf", ErrRange},
+ {"-3.4028236e38", "-Inf", ErrRange},
+ // the border is 3.40282356779...e+38
+ // borderline - okay
+ {"3.402823567e38", "3.4028235e+38", nil},
+ {"-3.402823567e38", "-3.4028235e+38", nil},
+ // borderline - too large
+ {"3.4028235678e38", "+Inf", ErrRange},
+ {"-3.4028235678e38", "-Inf", ErrRange},
+
+ // Denormals: less than 2^-126
+ {"1e-38", "1e-38", nil},
+ {"1e-39", "1e-39", nil},
+ {"1e-40", "1e-40", nil},
+ {"1e-41", "1e-41", nil},
+ {"1e-42", "1e-42", nil},
+ {"1e-43", "1e-43", nil},
+ {"1e-44", "1e-44", nil},
+ {"6e-45", "6e-45", nil}, // 4p-149 = 5.6e-45
+ {"5e-45", "6e-45", nil},
+ // Smallest denormal
+ {"1e-45", "1e-45", nil}, // 1p-149 = 1.4e-45
+ {"2e-45", "1e-45", nil},
+
+ // 2^92 = 8388608p+69 = 4951760157141521099596496896 (4.9517602e27)
+ // is an exact power of two that needs 8 decimal digits to be correctly
+ // parsed back.
+ // The float32 before is 16777215p+68 = 4.95175986e+27
+ // The halfway is 4.951760009. A bad algorithm that thinks the previous
+ // float32 is 8388607p+69 will shorten incorrectly to 4.95176e+27.
+ {"4951760157141521099596496896", "4.9517602e+27", nil},
+}
+
type atofSimpleTest struct {
x float64
s string
@@ -154,6 +203,12 @@ func init() {
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
rand.Seed(time.Now().UnixNano())
@@ -206,6 +261,19 @@ func testAtof(t *testing.T, opt bool) {
}
}
}
+ for _, test := range atof32tests {
+ 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)
+ }
+ }
SetOptimize(oldopt)
}
@@ -264,6 +332,35 @@ func TestRoundTrip(t *testing.T) {
}
}
+// TestRoundTrip32 tries a fraction of all finite positive float32 values.
+func TestRoundTrip32(t *testing.T) {
+ step := uint32(997)
+ if testing.Short() {
+ step = 99991
+ }
+ count := 0
+ for i := uint32(0); i < 0xff<<23; i += step {
+ f := math.Float32frombits(i)
+ if i&1 == 1 {
+ f = -f // negative
+ }
+ s := FormatFloat(float64(f), 'g', -1, 32)
+
+ parsed, err := ParseFloat(s, 32)
+ parsed32 := float32(parsed)
+ switch {
+ case err != nil:
+ t.Errorf("ParseFloat(%q, 32) gave error %s", s, err)
+ case float64(parsed32) != parsed:
+ t.Errorf("ParseFloat(%q, 32) = %v, not a float32 (nearest is %v)", s, parsed, parsed32)
+ case parsed32 != f:
+ t.Errorf("ParseFloat(%q, 32) = %b (expected %b)", s, parsed32, f)
+ }
+ count++
+ }
+ t.Logf("tested %d float32's", count)
+}
+
func BenchmarkAtof64Decimal(b *testing.B) {
for i := 0; i < b.N; i++ {
ParseFloat("33909", 64)
@@ -299,3 +396,35 @@ func BenchmarkAtof64RandomFloats(b *testing.B) {
ParseFloat(benchmarksRandomNormal[i%1024], 64)
}
}
+
+func BenchmarkAtof32Decimal(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ ParseFloat("33909", 32)
+ }
+}
+
+func BenchmarkAtof32Float(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ ParseFloat("339.778", 32)
+ }
+}
+
+func BenchmarkAtof32FloatExp(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ ParseFloat("12.3456e32", 32)
+ }
+}
+
+var float32strings [4096]string
+
+func BenchmarkAtof32Random(b *testing.B) {
+ n := uint32(997)
+ for i := range float32strings {
+ n = (99991*n + 42) % (0xff << 23)
+ float32strings[i] = FormatFloat(float64(math.Float32frombits(n)), 'g', -1, 32)
+ }
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ ParseFloat(float32strings[i%4096], 32)
+ }
+}
diff --git a/src/pkg/strconv/atoi.go b/src/pkg/strconv/atoi.go
index 59ef264d1..21c690096 100644
--- a/src/pkg/strconv/atoi.go
+++ b/src/pkg/strconv/atoi.go
@@ -20,7 +20,7 @@ type NumError struct {
}
func (e *NumError) Error() string {
- return "strconv." + e.Func + ": " + `parsing "` + e.Num + `": ` + e.Err.Error()
+ return "strconv." + e.Func + ": " + "parsing " + Quote(e.Num) + ": " + e.Err.Error()
}
func syntaxError(fn, str string) *NumError {
@@ -44,7 +44,7 @@ func cutoff64(base int) uint64 {
}
// ParseUint is like ParseInt but for unsigned numbers.
-func ParseUint(s string, b int, bitSize int) (n uint64, err error) {
+func ParseUint(s string, base int, bitSize int) (n uint64, err error) {
var cutoff, maxVal uint64
if bitSize == 0 {
@@ -57,32 +57,32 @@ func ParseUint(s string, b int, bitSize int) (n uint64, err error) {
err = ErrSyntax
goto Error
- case 2 <= b && b <= 36:
+ case 2 <= base && base <= 36:
// valid base; nothing to do
- case b == 0:
+ case base == 0:
// Look for octal, hex prefix.
switch {
case s[0] == '0' && len(s) > 1 && (s[1] == 'x' || s[1] == 'X'):
- b = 16
+ base = 16
s = s[2:]
if len(s) < 1 {
err = ErrSyntax
goto Error
}
case s[0] == '0':
- b = 8
+ base = 8
default:
- b = 10
+ base = 10
}
default:
- err = errors.New("invalid base " + Itoa(b))
+ err = errors.New("invalid base " + Itoa(base))
goto Error
}
n = 0
- cutoff = cutoff64(b)
+ cutoff = cutoff64(base)
maxVal = 1<<uint(bitSize) - 1
for i := 0; i < len(s); i++ {
@@ -100,19 +100,19 @@ func ParseUint(s string, b int, bitSize int) (n uint64, err error) {
err = ErrSyntax
goto Error
}
- if int(v) >= b {
+ if int(v) >= base {
n = 0
err = ErrSyntax
goto Error
}
if n >= cutoff {
- // n*b overflows
+ // n*base overflows
n = 1<<64 - 1
err = ErrRange
goto Error
}
- n *= uint64(b)
+ n *= uint64(base)
n1 := n + uint64(v)
if n1 < n || n1 > maxVal {
diff --git a/src/pkg/strconv/atoi_test.go b/src/pkg/strconv/atoi_test.go
index d0e7b61db..940757307 100644
--- a/src/pkg/strconv/atoi_test.go
+++ b/src/pkg/strconv/atoi_test.go
@@ -5,6 +5,7 @@
package strconv_test
import (
+ "errors"
"reflect"
. "strconv"
"testing"
@@ -146,6 +147,16 @@ var atoi32tests = []atoi32Test{
{"-2147483649", -1 << 31, ErrRange},
}
+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 atoi routines return NumErrors wrapping
// the error and the string. Convert the tables above.
@@ -277,6 +288,19 @@ func TestParseInt(t *testing.T) {
}
}
+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 BenchmarkAtoi(b *testing.B) {
for i := 0; i < b.N; i++ {
ParseInt("12345678", 10, 0)
diff --git a/src/pkg/strconv/decimal.go b/src/pkg/strconv/decimal.go
index a75071dcc..42601283d 100644
--- a/src/pkg/strconv/decimal.go
+++ b/src/pkg/strconv/decimal.go
@@ -79,7 +79,7 @@ func trim(a *decimal) {
// Assign v to a.
func (a *decimal) Assign(v uint64) {
- var buf [50]byte
+ var buf [24]byte
// Write reversed decimal in buf.
n := 0
diff --git a/src/pkg/strconv/extfloat.go b/src/pkg/strconv/extfloat.go
index aa5e5607c..b7eaaa61b 100644
--- a/src/pkg/strconv/extfloat.go
+++ b/src/pkg/strconv/extfloat.go
@@ -4,8 +4,6 @@
package strconv
-import "math"
-
// An extFloat represents an extended floating-point number, with more
// precision than a float64. It does not try to save bits: the
// number represented by the structure is mant*(2^exp), with a negative
@@ -127,8 +125,7 @@ var powersOfTen = [...]extFloat{
// floatBits returns the bits of the float64 that best approximates
// the extFloat passed as receiver. Overflow is set to true if
// the resulting float64 is ±Inf.
-func (f *extFloat) floatBits() (bits uint64, overflow bool) {
- flt := &float64info
+func (f *extFloat) floatBits(flt *floatInfo) (bits uint64, overflow bool) {
f.Normalize()
exp := f.exp + 63
@@ -140,7 +137,7 @@ func (f *extFloat) floatBits() (bits uint64, overflow bool) {
exp += n
}
- // Extract 1+flt.mantbits bits.
+ // Extract 1+flt.mantbits bits from the 64-bit mantissa.
mant := f.mant >> (63 - flt.mantbits)
if f.mant&(1<<(62-flt.mantbits)) != 0 {
// Round up.
@@ -155,22 +152,14 @@ func (f *extFloat) floatBits() (bits uint64, overflow bool) {
// Infinities.
if exp-flt.bias >= 1<<flt.expbits-1 {
- goto overflow
- }
-
- // Denormalized?
- if mant&(1<<flt.mantbits) == 0 {
+ // ±Inf
+ mant = 0
+ exp = 1<<flt.expbits - 1 + flt.bias
+ overflow = true
+ } else if mant&(1<<flt.mantbits) == 0 {
+ // Denormalized?
exp = flt.bias
}
- goto out
-
-overflow:
- // ±Inf
- mant = 0
- exp = 1<<flt.expbits - 1 + flt.bias
- overflow = true
-
-out:
// Assemble bits.
bits = mant & (uint64(1)<<flt.mantbits - 1)
bits |= uint64((exp-flt.bias)&(1<<flt.expbits-1)) << flt.mantbits
@@ -180,40 +169,24 @@ out:
return
}
-// Assign sets f to the value of x.
-func (f *extFloat) Assign(x float64) {
- if x < 0 {
- x = -x
- f.neg = true
- }
- x, f.exp = math.Frexp(x)
- f.mant = uint64(x * float64(1<<64))
- f.exp -= 64
-}
-
-// AssignComputeBounds sets f to the value of x and returns
+// AssignComputeBounds sets f to the floating point value
+// defined by mant, exp and precision given by flt. It returns
// lower, upper such that any number in the closed interval
-// [lower, upper] is converted back to x.
-func (f *extFloat) AssignComputeBounds(x float64) (lower, upper extFloat) {
- // Special cases.
- bits := math.Float64bits(x)
- flt := &float64info
- neg := bits>>(flt.expbits+flt.mantbits) != 0
- expBiased := int(bits>>flt.mantbits) & (1<<flt.expbits - 1)
- mant := bits & (uint64(1)<<flt.mantbits - 1)
-
- if expBiased == 0 {
- // denormalized.
- f.mant = mant
- f.exp = 1 + flt.bias - int(flt.mantbits)
- } else {
- f.mant = mant | 1<<flt.mantbits
- f.exp = expBiased + flt.bias - int(flt.mantbits)
- }
+// [lower, upper] is converted back to the same floating point number.
+func (f *extFloat) AssignComputeBounds(mant uint64, exp int, neg bool, flt *floatInfo) (lower, upper extFloat) {
+ f.mant = mant
+ f.exp = exp - int(flt.mantbits)
f.neg = neg
+ if f.exp <= 0 && mant == (mant>>uint(-f.exp))<<uint(-f.exp) {
+ // An exact integer
+ f.mant >>= uint(-f.exp)
+ f.exp = 0
+ return *f, *f
+ }
+ expBiased := exp - flt.bias
upper = extFloat{mant: 2*f.mant + 1, exp: f.exp - 1, neg: f.neg}
- if mant != 0 || expBiased == 1 {
+ if mant != 1<<flt.mantbits || expBiased == 1 {
lower = extFloat{mant: 2*f.mant - 1, exp: f.exp - 1, neg: f.neg}
} else {
lower = extFloat{mant: 4*f.mant - 1, exp: f.exp - 2, neg: f.neg}
@@ -223,20 +196,38 @@ func (f *extFloat) AssignComputeBounds(x float64) (lower, upper extFloat) {
// Normalize normalizes f so that the highest bit of the mantissa is
// set, and returns the number by which the mantissa was left-shifted.
-func (f *extFloat) Normalize() uint {
- if f.mant == 0 {
+func (f *extFloat) Normalize() (shift uint) {
+ mant, exp := f.mant, f.exp
+ if mant == 0 {
return 0
}
- exp_before := f.exp
- for f.mant < (1 << 55) {
- f.mant <<= 8
- f.exp -= 8
+ if mant>>(64-32) == 0 {
+ mant <<= 32
+ exp -= 32
+ }
+ if mant>>(64-16) == 0 {
+ mant <<= 16
+ exp -= 16
}
- for f.mant < (1 << 63) {
- f.mant <<= 1
- f.exp -= 1
+ if mant>>(64-8) == 0 {
+ mant <<= 8
+ exp -= 8
}
- return uint(exp_before - f.exp)
+ if mant>>(64-4) == 0 {
+ mant <<= 4
+ exp -= 4
+ }
+ if mant>>(64-2) == 0 {
+ mant <<= 2
+ exp -= 2
+ }
+ if mant>>(64-1) == 0 {
+ mant <<= 1
+ exp -= 1
+ }
+ shift = uint(f.exp - exp)
+ f.mant, f.exp = mant, exp
+ return
}
// Multiply sets f to the product f*g: the result is correctly rounded,
@@ -264,24 +255,22 @@ var uint64pow10 = [...]uint64{
1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
}
-// AssignDecimal sets f to an approximate value of the decimal d. It
+// AssignDecimal sets f to an approximate value mantissa*10^exp. It
// returns true if the value represented by f is guaranteed to be the
-// best approximation of d after being rounded to a float64.
-func (f *extFloat) AssignDecimal(d *decimal) (ok bool) {
+// best approximation of d after being rounded to a float64 or
+// float32 depending on flt.
+func (f *extFloat) AssignDecimal(mantissa uint64, exp10 int, neg bool, trunc bool, flt *floatInfo) (ok bool) {
const uint64digits = 19
const errorscale = 8
- mant10, digits := d.atou64()
- exp10 := d.dp - digits
errors := 0 // An upper bound for error, computed in errorscale*ulp.
-
- if digits < d.nd {
+ if trunc {
// the decimal number was truncated.
errors += errorscale / 2
}
- f.mant = mant10
+ f.mant = mantissa
f.exp = 0
- f.neg = d.neg
+ f.neg = neg
// Multiply by powers of ten.
i := (exp10 - firstPowerOfTen) / stepPowerOfTen
@@ -291,9 +280,9 @@ func (f *extFloat) AssignDecimal(d *decimal) (ok bool) {
adjExp := (exp10 - firstPowerOfTen) % stepPowerOfTen
// We multiply by exp%step
- if digits+adjExp <= uint64digits {
- // We can multiply the mantissa
- f.mant *= uint64(float64pow10[adjExp])
+ if adjExp < uint64digits && mantissa < uint64pow10[uint64digits-adjExp] {
+ // We can multiply the mantissa exactly.
+ f.mant *= uint64pow10[adjExp]
f.Normalize()
} else {
f.Normalize()
@@ -318,10 +307,10 @@ func (f *extFloat) AssignDecimal(d *decimal) (ok bool) {
// The 64 bits mantissa is 1 + 52 bits for float64 + 11 extra bits.
//
// In many cases the approximation will be good enough.
- const denormalExp = -1023 - 63
- flt := &float64info
+ denormalExp := flt.bias - 63
var extrabits uint
if f.exp <= denormalExp {
+ // f.mant * 2^f.exp is smaller than 2^(flt.bias+1).
extrabits = uint(63 - flt.mantbits + 1 + uint(denormalExp-f.exp))
} else {
extrabits = uint(63 - flt.mantbits)
@@ -344,16 +333,17 @@ func (f *extFloat) AssignDecimal(d *decimal) (ok bool) {
// f by an approximate power of ten 10^-exp, and returns exp10, so
// that f*10^exp10 has the same value as the old f, up to an ulp,
// as well as the index of 10^-exp in the powersOfTen table.
-// The arguments expMin and expMax constrain the final value of the
-// binary exponent of f.
-func (f *extFloat) frexp10(expMin, expMax int) (exp10, index int) {
- // it is illegal to call this function with a too restrictive exponent range.
- if expMax-expMin <= 25 {
- panic("strconv: invalid exponent range")
- }
+func (f *extFloat) frexp10() (exp10, index int) {
+ // The constants expMin and expMax constrain the final value of the
+ // binary exponent of f. We want a small integral part in the result
+ // because finding digits of an integer requires divisions, whereas
+ // digits of the fractional part can be found by repeatedly multiplying
+ // by 10.
+ const expMin = -60
+ const expMax = -32
// Find power of ten such that x * 10^n has a binary exponent
- // between expMin and expMax
- approxExp10 := -(f.exp + 100) * 28 / 93 // log(10)/log(2) is close to 93/28.
+ // between expMin and expMax.
+ approxExp10 := ((expMin+expMax)/2 - f.exp) * 28 / 93 // log(10)/log(2) is close to 93/28.
i := (approxExp10 - firstPowerOfTen) / stepPowerOfTen
Loop:
for {
@@ -375,26 +365,202 @@ Loop:
}
// frexp10Many applies a common shift by a power of ten to a, b, c.
-func frexp10Many(expMin, expMax int, a, b, c *extFloat) (exp10 int) {
- exp10, i := c.frexp10(expMin, expMax)
+func frexp10Many(a, b, c *extFloat) (exp10 int) {
+ exp10, i := c.frexp10()
a.Multiply(powersOfTen[i])
b.Multiply(powersOfTen[i])
return
}
+// FixedDecimal stores in d the first n significant digits
+// of the decimal representation of f. It returns false
+// if it cannot be sure of the answer.
+func (f *extFloat) FixedDecimal(d *decimalSlice, n int) bool {
+ if f.mant == 0 {
+ d.nd = 0
+ d.dp = 0
+ d.neg = f.neg
+ return true
+ }
+ if n == 0 {
+ panic("strconv: internal error: extFloat.FixedDecimal called with n == 0")
+ }
+ // Multiply by an appropriate power of ten to have a reasonable
+ // number to process.
+ f.Normalize()
+ exp10, _ := f.frexp10()
+
+ shift := uint(-f.exp)
+ integer := uint32(f.mant >> shift)
+ fraction := f.mant - (uint64(integer) << shift)
+ ε := uint64(1) // ε is the uncertainty we have on the mantissa of f.
+
+ // Write exactly n digits to d.
+ needed := n // how many digits are left to write.
+ integerDigits := 0 // the number of decimal digits of integer.
+ pow10 := uint64(1) // the power of ten by which f was scaled.
+ for i, pow := 0, uint64(1); i < 20; i++ {
+ if pow > uint64(integer) {
+ integerDigits = i
+ break
+ }
+ pow *= 10
+ }
+ rest := integer
+ if integerDigits > needed {
+ // the integral part is already large, trim the last digits.
+ pow10 = uint64pow10[integerDigits-needed]
+ integer /= uint32(pow10)
+ rest -= integer * uint32(pow10)
+ } else {
+ rest = 0
+ }
+
+ // Write the digits of integer: the digits of rest are omitted.
+ var buf [32]byte
+ pos := len(buf)
+ for v := integer; v > 0; {
+ v1 := v / 10
+ v -= 10 * v1
+ pos--
+ buf[pos] = byte(v + '0')
+ v = v1
+ }
+ for i := pos; i < len(buf); i++ {
+ d.d[i-pos] = buf[i]
+ }
+ nd := len(buf) - pos
+ d.nd = nd
+ d.dp = integerDigits + exp10
+ needed -= nd
+
+ if needed > 0 {
+ if rest != 0 || pow10 != 1 {
+ panic("strconv: internal error, rest != 0 but needed > 0")
+ }
+ // Emit digits for the fractional part. Each time, 10*fraction
+ // fits in a uint64 without overflow.
+ for needed > 0 {
+ fraction *= 10
+ ε *= 10 // the uncertainty scales as we multiply by ten.
+ if 2*ε > 1<<shift {
+ // the error is so large it could modify which digit to write, abort.
+ return false
+ }
+ digit := fraction >> shift
+ d.d[nd] = byte(digit + '0')
+ fraction -= digit << shift
+ nd++
+ needed--
+ }
+ d.nd = nd
+ }
+
+ // We have written a truncation of f (a numerator / 10^d.dp). The remaining part
+ // can be interpreted as a small number (< 1) to be added to the last digit of the
+ // numerator.
+ //
+ // If rest > 0, the amount is:
+ // (rest<<shift | fraction) / (pow10 << shift)
+ // fraction being known with a ±ε uncertainty.
+ // The fact that n > 0 guarantees that pow10 << shift does not overflow a uint64.
+ //
+ // If rest = 0, pow10 == 1 and the amount is
+ // fraction / (1 << shift)
+ // fraction being known with a ±ε uncertainty.
+ //
+ // We pass this information to the rounding routine for adjustment.
+
+ ok := adjustLastDigitFixed(d, uint64(rest)<<shift|fraction, pow10, shift, ε)
+ if !ok {
+ return false
+ }
+ // Trim trailing zeros.
+ for i := d.nd - 1; i >= 0; i-- {
+ if d.d[i] != '0' {
+ d.nd = i + 1
+ break
+ }
+ }
+ return true
+}
+
+// adjustLastDigitFixed assumes d contains the representation of the integral part
+// of some number, whose fractional part is num / (den << shift). The numerator
+// num is only known up to an uncertainty of size ε, assumed to be less than
+// (den << shift)/2.
+//
+// It will increase the last digit by one to account for correct rounding, typically
+// when the fractional part is greater than 1/2, and will return false if ε is such
+// that no correct answer can be given.
+func adjustLastDigitFixed(d *decimalSlice, num, den uint64, shift uint, ε uint64) bool {
+ if num > den<<shift {
+ panic("strconv: num > den<<shift in adjustLastDigitFixed")
+ }
+ if 2*ε > den<<shift {
+ panic("strconv: ε > (den<<shift)/2")
+ }
+ if 2*(num+ε) < den<<shift {
+ return true
+ }
+ if 2*(num-ε) > den<<shift {
+ // increment d by 1.
+ i := d.nd - 1
+ for ; i >= 0; i-- {
+ if d.d[i] == '9' {
+ d.nd--
+ } else {
+ break
+ }
+ }
+ if i < 0 {
+ d.d[0] = '1'
+ d.nd = 1
+ d.dp++
+ } else {
+ d.d[i]++
+ }
+ return true
+ }
+ return false
+}
+
// ShortestDecimal stores in d the shortest decimal representation of f
// which belongs to the open interval (lower, upper), where f is supposed
// to lie. It returns false whenever the result is unsure. The implementation
// uses the Grisu3 algorithm.
-func (f *extFloat) ShortestDecimal(d *decimal, lower, upper *extFloat) bool {
+func (f *extFloat) ShortestDecimal(d *decimalSlice, lower, upper *extFloat) bool {
if f.mant == 0 {
- d.d[0] = '0'
- d.nd = 1
+ d.nd = 0
d.dp = 0
d.neg = f.neg
+ return true
+ }
+ if f.exp == 0 && *lower == *f && *lower == *upper {
+ // an exact integer.
+ var buf [24]byte
+ n := len(buf) - 1
+ for v := f.mant; v > 0; {
+ v1 := v / 10
+ v -= 10 * v1
+ buf[n] = byte(v + '0')
+ n--
+ v = v1
+ }
+ nd := len(buf) - n - 1
+ for i := 0; i < nd; i++ {
+ d.d[i] = buf[n+1+i]
+ }
+ d.nd, d.dp = nd, nd
+ for d.nd > 0 && d.d[d.nd-1] == '0' {
+ d.nd--
+ }
+ if d.nd == 0 {
+ d.dp = 0
+ }
+ d.neg = f.neg
+ return true
}
- const minExp = -60
- const maxExp = -32
upper.Normalize()
// Uniformize exponents.
if f.exp > upper.exp {
@@ -406,7 +572,7 @@ func (f *extFloat) ShortestDecimal(d *decimal, lower, upper *extFloat) bool {
lower.exp = upper.exp
}
- exp10 := frexp10Many(minExp, maxExp, lower, f, upper)
+ exp10 := frexp10Many(lower, f, upper)
// Take a safety margin due to rounding in frexp10Many, but we lose precision.
upper.mant++
lower.mant--
@@ -424,10 +590,12 @@ func (f *extFloat) ShortestDecimal(d *decimal, lower, upper *extFloat) bool {
// Count integral digits: there are at most 10.
var integerDigits int
- for i, pow := range uint64pow10 {
- if uint64(integer) >= pow {
- integerDigits = i + 1
+ for i, pow := 0, uint64(1); i < 20; i++ {
+ if pow > uint64(integer) {
+ integerDigits = i
+ break
}
+ pow *= 10
}
for i := 0; i < integerDigits; i++ {
pow := uint64pow10[integerDigits-i-1]
@@ -471,11 +639,11 @@ func (f *extFloat) ShortestDecimal(d *decimal, lower, upper *extFloat) bool {
return false
}
-// adjustLastDigit modifies d = x-currentDiff*ε, to get closest to
+// adjustLastDigit modifies d = x-currentDiff*ε, to get closest to
// d = x-targetDiff*ε, without becoming smaller than x-maxDiff*ε.
// It assumes that a decimal digit is worth ulpDecimal*ε, and that
// all data is known with a error estimate of ulpBinary*ε.
-func adjustLastDigit(d *decimal, currentDiff, targetDiff, maxDiff, ulpDecimal, ulpBinary uint64) bool {
+func adjustLastDigit(d *decimalSlice, currentDiff, targetDiff, maxDiff, ulpDecimal, ulpBinary uint64) bool {
if ulpDecimal < 2*ulpBinary {
// Approximation is too wide.
return false
diff --git a/src/pkg/strconv/fp_test.go b/src/pkg/strconv/fp_test.go
index 171defa44..6de2f8bc6 100644
--- a/src/pkg/strconv/fp_test.go
+++ b/src/pkg/strconv/fp_test.go
@@ -7,7 +7,6 @@ package strconv_test
import (
"bufio"
"fmt"
- "io"
"os"
"strconv"
"strings"
@@ -96,31 +95,22 @@ func myatof32(s string) (f float32, ok bool) {
}
func TestFp(t *testing.T) {
- f, err := os.Open("testfp.txt")
+ f, err := os.Open("testdata/testfp.txt")
if err != nil {
- t.Fatal("testfp: open testfp.txt:", err)
+ t.Fatal("testfp: open testdata/testfp.txt:", err)
}
defer f.Close()
- b := bufio.NewReader(f)
+ s := bufio.NewScanner(f)
- lineno := 0
- for {
- line, err2 := b.ReadString('\n')
- if err2 == io.EOF {
- break
- }
- if err2 != nil {
- t.Fatal("testfp: read testfp.txt: " + err2.Error())
- }
- line = line[0 : len(line)-1]
- lineno++
+ for lineno := 1; s.Scan(); lineno++ {
+ line := s.Text()
if len(line) == 0 || line[0] == '#' {
continue
}
a := strings.Split(line, " ")
if len(a) != 4 {
- t.Error("testfp.txt:", lineno, ": wrong field count")
+ t.Error("testdata/testfp.txt:", lineno, ": wrong field count")
continue
}
var s string
@@ -130,22 +120,25 @@ func TestFp(t *testing.T) {
var ok bool
v, ok = myatof64(a[2])
if !ok {
- t.Error("testfp.txt:", lineno, ": cannot atof64 ", a[2])
+ t.Error("testdata/testfp.txt:", lineno, ": cannot atof64 ", a[2])
continue
}
s = fmt.Sprintf(a[1], v)
case "float32":
v1, ok := myatof32(a[2])
if !ok {
- t.Error("testfp.txt:", lineno, ": cannot atof32 ", a[2])
+ t.Error("testdata/testfp.txt:", lineno, ": cannot atof32 ", a[2])
continue
}
s = fmt.Sprintf(a[1], v1)
v = float64(v1)
}
if s != a[3] {
- t.Error("testfp.txt:", lineno, ": ", a[0], " ", a[1], " ", a[2], " (", v, ") ",
+ t.Error("testdata/testfp.txt:", lineno, ": ", a[0], " ", a[1], " ", a[2], " (", v, ") ",
"want ", a[3], " got ", s)
}
}
+ if s.Err() != nil {
+ t.Fatal("testfp: read testdata/testfp.txt: ", s.Err())
+ }
}
diff --git a/src/pkg/strconv/ftoa.go b/src/pkg/strconv/ftoa.go
index 8eefbee79..1a9c41b85 100644
--- a/src/pkg/strconv/ftoa.go
+++ b/src/pkg/strconv/ftoa.go
@@ -98,42 +98,79 @@ func genericFtoa(dst []byte, val float64, fmt byte, prec, bitSize int) []byte {
return fmtB(dst, neg, mant, exp, flt)
}
+ if !optimize {
+ return bigFtoa(dst, prec, fmt, neg, mant, exp, flt)
+ }
+
+ var digs decimalSlice
+ ok := false
// Negative precision means "only as much as needed to be exact."
shortest := prec < 0
-
- d := new(decimal)
if shortest {
- ok := false
- if optimize && bitSize == 64 {
- // Try Grisu3 algorithm.
- f := new(extFloat)
- lower, upper := f.AssignComputeBounds(val)
- ok = f.ShortestDecimal(d, &lower, &upper)
- }
+ // Try Grisu3 algorithm.
+ f := new(extFloat)
+ lower, upper := f.AssignComputeBounds(mant, exp, neg, flt)
+ var buf [32]byte
+ digs.d = buf[:]
+ ok = f.ShortestDecimal(&digs, &lower, &upper)
if !ok {
- // Create exact decimal representation.
- // The shift is exp - flt.mantbits because mant is a 1-bit integer
- // followed by a flt.mantbits fraction, and we are treating it as
- // a 1+flt.mantbits-bit integer.
- d.Assign(mant)
- d.Shift(exp - int(flt.mantbits))
- roundShortest(d, mant, exp, flt)
+ return bigFtoa(dst, prec, fmt, neg, mant, exp, flt)
}
// Precision for shortest representation mode.
- if prec < 0 {
- switch fmt {
- case 'e', 'E':
- prec = d.nd - 1
- case 'f':
- prec = max(d.nd-d.dp, 0)
- case 'g', 'G':
- prec = d.nd
+ switch fmt {
+ case 'e', 'E':
+ prec = digs.nd - 1
+ case 'f':
+ prec = max(digs.nd-digs.dp, 0)
+ case 'g', 'G':
+ prec = digs.nd
+ }
+ } else if fmt != 'f' {
+ // Fixed number of digits.
+ digits := prec
+ switch fmt {
+ case 'e', 'E':
+ digits++
+ case 'g', 'G':
+ if prec == 0 {
+ prec = 1
}
+ digits = prec
+ }
+ if digits <= 15 {
+ // try fast algorithm when the number of digits is reasonable.
+ var buf [24]byte
+ digs.d = buf[:]
+ f := extFloat{mant, exp - int(flt.mantbits), neg}
+ ok = f.FixedDecimal(&digs, digits)
+ }
+ }
+ if !ok {
+ return bigFtoa(dst, prec, fmt, neg, mant, exp, flt)
+ }
+ return formatDigits(dst, shortest, neg, digs, prec, fmt)
+}
+
+// bigFtoa uses multiprecision computations to format a float.
+func bigFtoa(dst []byte, prec int, fmt byte, neg bool, mant uint64, exp int, flt *floatInfo) []byte {
+ d := new(decimal)
+ d.Assign(mant)
+ d.Shift(exp - int(flt.mantbits))
+ var digs decimalSlice
+ shortest := prec < 0
+ if shortest {
+ roundShortest(d, mant, exp, flt)
+ digs = decimalSlice{d: d.d[:], nd: d.nd, dp: d.dp}
+ // Precision for shortest representation mode.
+ switch fmt {
+ case 'e', 'E':
+ prec = digs.nd - 1
+ case 'f':
+ prec = max(digs.nd-digs.dp, 0)
+ case 'g', 'G':
+ prec = digs.nd
}
} else {
- // Create exact decimal representation.
- d.Assign(mant)
- d.Shift(exp - int(flt.mantbits))
// Round appropriately.
switch fmt {
case 'e', 'E':
@@ -146,18 +183,22 @@ func genericFtoa(dst []byte, val float64, fmt byte, prec, bitSize int) []byte {
}
d.Round(prec)
}
+ digs = decimalSlice{d: d.d[:], nd: d.nd, dp: d.dp}
}
+ return formatDigits(dst, shortest, neg, digs, prec, fmt)
+}
+func formatDigits(dst []byte, shortest bool, neg bool, digs decimalSlice, prec int, fmt byte) []byte {
switch fmt {
case 'e', 'E':
- return fmtE(dst, neg, d, prec, fmt)
+ return fmtE(dst, neg, digs, prec, fmt)
case 'f':
- return fmtF(dst, neg, d, prec)
+ return fmtF(dst, neg, digs, prec)
case 'g', 'G':
// trailing fractional zeros in 'e' form will be trimmed.
eprec := prec
- if eprec > d.nd && d.nd >= d.dp {
- eprec = d.nd
+ if eprec > digs.nd && digs.nd >= digs.dp {
+ eprec = digs.nd
}
// %e is used if the exponent from the conversion
// is less than -4 or greater than or equal to the precision.
@@ -165,17 +206,17 @@ func genericFtoa(dst []byte, val float64, fmt byte, prec, bitSize int) []byte {
if shortest {
eprec = 6
}
- exp := d.dp - 1
+ exp := digs.dp - 1
if exp < -4 || exp >= eprec {
- if prec > d.nd {
- prec = d.nd
+ if prec > digs.nd {
+ prec = digs.nd
}
- return fmtE(dst, neg, d, prec-1, fmt+'e'-'g')
+ return fmtE(dst, neg, digs, prec-1, fmt+'e'-'g')
}
- if prec > d.dp {
- prec = d.nd
+ if prec > digs.dp {
+ prec = digs.nd
}
- return fmtF(dst, neg, d, max(prec-d.dp, 0))
+ return fmtF(dst, neg, digs, max(prec-digs.dp, 0))
}
// unknown format
@@ -214,7 +255,7 @@ func roundShortest(d *decimal, mant uint64, exp int, flt *floatInfo) {
// d = mant << (exp - mantbits)
// Next highest floating point number is mant+1 << exp-mantbits.
- // Our upper bound is halfway inbetween, mant*2+1 << exp-mantbits-1.
+ // Our upper bound is halfway between, mant*2+1 << exp-mantbits-1.
upper := new(decimal)
upper.Assign(mant*2 + 1)
upper.Shift(exp - int(flt.mantbits) - 1)
@@ -224,7 +265,7 @@ func roundShortest(d *decimal, mant uint64, exp int, flt *floatInfo) {
// unless mant-1 drops the significant bit and exp is not the minimum exp,
// in which case the next lowest is mant*2-1 << exp-mantbits-1.
// Either way, call it mantlo << explo-mantbits.
- // Our lower bound is halfway inbetween, mantlo*2+1 << explo-mantbits-1.
+ // Our lower bound is halfway between, mantlo*2+1 << explo-mantbits-1.
var mantlo uint64
var explo int
if mant > 1<<flt.mantbits || exp == minexp {
@@ -283,8 +324,14 @@ func roundShortest(d *decimal, mant uint64, exp int, flt *floatInfo) {
}
}
+type decimalSlice struct {
+ d []byte
+ nd, dp int
+ neg bool
+}
+
// %e: -d.ddddde±dd
-func fmtE(dst []byte, neg bool, d *decimal, prec int, fmt byte) []byte {
+func fmtE(dst []byte, neg bool, d decimalSlice, prec int, fmt byte) []byte {
// sign
if neg {
dst = append(dst, '-')
@@ -300,12 +347,15 @@ func fmtE(dst []byte, neg bool, d *decimal, prec int, fmt byte) []byte {
// .moredigits
if prec > 0 {
dst = append(dst, '.')
- for i := 1; i <= prec; i++ {
- ch = '0'
- if i < d.nd {
- ch = d.d[i]
- }
- dst = append(dst, ch)
+ i := 1
+ m := d.nd + prec + 1 - max(d.nd, prec+1)
+ for i < m {
+ dst = append(dst, d.d[i])
+ i++
+ }
+ for i <= prec {
+ dst = append(dst, '0')
+ i++
}
}
@@ -335,17 +385,20 @@ func fmtE(dst []byte, neg bool, d *decimal, prec int, fmt byte) []byte {
i--
buf[i] = byte(exp + '0')
- // leading zeroes
- if i > len(buf)-2 {
- i--
- buf[i] = '0'
+ switch i {
+ case 0:
+ dst = append(dst, buf[0], buf[1], buf[2])
+ case 1:
+ dst = append(dst, buf[1], buf[2])
+ case 2:
+ // leading zeroes
+ dst = append(dst, '0', buf[2])
}
-
- return append(dst, buf[i:]...)
+ return dst
}
// %f: -ddddddd.ddddd
-func fmtF(dst []byte, neg bool, d *decimal, prec int) []byte {
+func fmtF(dst []byte, neg bool, d decimalSlice, prec int) []byte {
// sign
if neg {
dst = append(dst, '-')
diff --git a/src/pkg/strconv/ftoa_test.go b/src/pkg/strconv/ftoa_test.go
index 7d8617a85..39b861547 100644
--- a/src/pkg/strconv/ftoa_test.go
+++ b/src/pkg/strconv/ftoa_test.go
@@ -163,6 +163,7 @@ func TestFtoaRandom(t *testing.T) {
for i := 0; i < N; i++ {
bits := uint64(rand.Uint32())<<32 | uint64(rand.Uint32())
x := math.Float64frombits(bits)
+
shortFast := FormatFloat(x, 'g', -1, 64)
SetOptimize(false)
shortSlow := FormatFloat(x, 'g', -1, 64)
@@ -170,23 +171,15 @@ func TestFtoaRandom(t *testing.T) {
if shortSlow != shortFast {
t.Errorf("%b printed as %s, want %s", x, shortFast, shortSlow)
}
- }
-}
-func TestAppendFloatDoesntAllocate(t *testing.T) {
- n := numAllocations(func() {
- var buf [64]byte
- AppendFloat(buf[:0], 1.23, 'g', 5, 64)
- })
- want := 1 // TODO(bradfitz): this might be 0, once escape analysis is better
- if n != want {
- t.Errorf("with local buffer, did %d allocations, want %d", n, want)
- }
- n = numAllocations(func() {
- AppendFloat(globalBuf[:0], 1.23, 'g', 5, 64)
- })
- if n != 0 {
- t.Errorf("with reused buffer, did %d allocations, want 0", n)
+ prec := rand.Intn(12) + 5
+ shortFast = FormatFloat(x, 'e', prec, 64)
+ SetOptimize(false)
+ shortSlow = FormatFloat(x, 'e', prec, 64)
+ SetOptimize(true)
+ if shortSlow != shortFast {
+ t.Errorf("%b printed as %s, want %s", x, shortFast, shortSlow)
+ }
}
}
@@ -220,37 +213,28 @@ func BenchmarkFormatFloatBig(b *testing.B) {
}
}
-func BenchmarkAppendFloatDecimal(b *testing.B) {
- dst := make([]byte, 0, 30)
+func benchmarkAppendFloat(b *testing.B, f float64, fmt byte, prec, bitSize int) {
+ dst := make([]byte, 30)
for i := 0; i < b.N; i++ {
- AppendFloat(dst, 33909, 'g', -1, 64)
+ AppendFloat(dst[:0], f, fmt, prec, bitSize)
}
}
-func BenchmarkAppendFloat(b *testing.B) {
- dst := make([]byte, 0, 30)
- for i := 0; i < b.N; i++ {
- AppendFloat(dst, 339.7784, 'g', -1, 64)
- }
-}
-
-func BenchmarkAppendFloatExp(b *testing.B) {
- dst := make([]byte, 0, 30)
- for i := 0; i < b.N; i++ {
- AppendFloat(dst, -5.09e75, 'g', -1, 64)
- }
+func BenchmarkAppendFloatDecimal(b *testing.B) { benchmarkAppendFloat(b, 33909, 'g', -1, 64) }
+func BenchmarkAppendFloat(b *testing.B) { benchmarkAppendFloat(b, 339.7784, 'g', -1, 64) }
+func BenchmarkAppendFloatExp(b *testing.B) { benchmarkAppendFloat(b, -5.09e75, 'g', -1, 64) }
+func BenchmarkAppendFloatNegExp(b *testing.B) { benchmarkAppendFloat(b, -5.11e-95, 'g', -1, 64) }
+func BenchmarkAppendFloatBig(b *testing.B) {
+ benchmarkAppendFloat(b, 123456789123456789123456789, 'g', -1, 64)
}
-func BenchmarkAppendFloatNegExp(b *testing.B) {
- dst := make([]byte, 0, 30)
- for i := 0; i < b.N; i++ {
- AppendFloat(dst, -5.11e-95, 'g', -1, 64)
- }
-}
+func BenchmarkAppendFloat32Integer(b *testing.B) { benchmarkAppendFloat(b, 33909, 'g', -1, 32) }
+func BenchmarkAppendFloat32ExactFraction(b *testing.B) { benchmarkAppendFloat(b, 3.375, 'g', -1, 32) }
+func BenchmarkAppendFloat32Point(b *testing.B) { benchmarkAppendFloat(b, 339.7784, 'g', -1, 32) }
+func BenchmarkAppendFloat32Exp(b *testing.B) { benchmarkAppendFloat(b, -5.09e25, 'g', -1, 32) }
+func BenchmarkAppendFloat32NegExp(b *testing.B) { benchmarkAppendFloat(b, -5.11e-25, 'g', -1, 32) }
-func BenchmarkAppendFloatBig(b *testing.B) {
- dst := make([]byte, 0, 30)
- for i := 0; i < b.N; i++ {
- AppendFloat(dst, 123456789123456789123456789, 'g', -1, 64)
- }
-}
+func BenchmarkAppendFloat64Fixed1(b *testing.B) { benchmarkAppendFloat(b, 123456, 'e', 3, 64) }
+func BenchmarkAppendFloat64Fixed2(b *testing.B) { benchmarkAppendFloat(b, 123.456, 'e', 3, 64) }
+func BenchmarkAppendFloat64Fixed3(b *testing.B) { benchmarkAppendFloat(b, 1.23456e+78, 'e', 3, 64) }
+func BenchmarkAppendFloat64Fixed4(b *testing.B) { benchmarkAppendFloat(b, 1.23456e-78, 'e', 3, 64) }
diff --git a/src/pkg/strconv/isprint.go b/src/pkg/strconv/isprint.go
index a03a07bfb..db5f0fbae 100644
--- a/src/pkg/strconv/isprint.go
+++ b/src/pkg/strconv/isprint.go
@@ -3,7 +3,7 @@
package strconv
-// (474+134+42)*2 + (180)*4 = 2020 bytes
+// (470+136+60)*2 + (218)*4 = 2204 bytes
var isPrint16 = []uint16{
0x0020, 0x007e,
@@ -12,7 +12,7 @@ var isPrint16 = []uint16{
0x0384, 0x0527,
0x0531, 0x0556,
0x0559, 0x058a,
- 0x0591, 0x05c7,
+ 0x058f, 0x05c7,
0x05d0, 0x05ea,
0x05f0, 0x05f4,
0x0606, 0x061b,
@@ -23,7 +23,8 @@ var isPrint16 = []uint16{
0x0800, 0x082d,
0x0830, 0x085b,
0x085e, 0x085e,
- 0x0900, 0x098c,
+ 0x08a0, 0x08ac,
+ 0x08e4, 0x098c,
0x098f, 0x0990,
0x0993, 0x09b2,
0x09b6, 0x09b9,
@@ -99,12 +100,12 @@ var isPrint16 = []uint16{
0x0eaa, 0x0ebd,
0x0ec0, 0x0ecd,
0x0ed0, 0x0ed9,
- 0x0edc, 0x0edd,
+ 0x0edc, 0x0edf,
0x0f00, 0x0f6c,
0x0f71, 0x0fda,
- 0x1000, 0x10c5,
- 0x10d0, 0x10fc,
- 0x1100, 0x124d,
+ 0x1000, 0x10c7,
+ 0x10cd, 0x10cd,
+ 0x10d0, 0x124d,
0x1250, 0x125d,
0x1260, 0x128d,
0x1290, 0x12b5,
@@ -120,8 +121,7 @@ var isPrint16 = []uint16{
0x1720, 0x1736,
0x1740, 0x1753,
0x1760, 0x1773,
- 0x1780, 0x17b3,
- 0x17b6, 0x17dd,
+ 0x1780, 0x17dd,
0x17e0, 0x17e9,
0x17f0, 0x17f9,
0x1800, 0x180d,
@@ -145,13 +145,12 @@ var isPrint16 = []uint16{
0x1aa0, 0x1aad,
0x1b00, 0x1b4b,
0x1b50, 0x1b7c,
- 0x1b80, 0x1baa,
- 0x1bae, 0x1bb9,
- 0x1bc0, 0x1bf3,
+ 0x1b80, 0x1bf3,
0x1bfc, 0x1c37,
0x1c3b, 0x1c49,
0x1c4d, 0x1c7f,
- 0x1cd0, 0x1cf2,
+ 0x1cc0, 0x1cc7,
+ 0x1cd0, 0x1cf6,
0x1d00, 0x1de6,
0x1dfc, 0x1f15,
0x1f18, 0x1f1d,
@@ -165,7 +164,7 @@ var isPrint16 = []uint16{
0x2030, 0x205e,
0x2070, 0x2071,
0x2074, 0x209c,
- 0x20a0, 0x20b9,
+ 0x20a0, 0x20ba,
0x20d0, 0x20f0,
0x2100, 0x2189,
0x2190, 0x23f3,
@@ -173,12 +172,13 @@ var isPrint16 = []uint16{
0x2440, 0x244a,
0x2460, 0x2b4c,
0x2b50, 0x2b59,
- 0x2c00, 0x2cf1,
- 0x2cf9, 0x2d25,
- 0x2d30, 0x2d65,
+ 0x2c00, 0x2cf3,
+ 0x2cf9, 0x2d27,
+ 0x2d2d, 0x2d2d,
+ 0x2d30, 0x2d67,
0x2d6f, 0x2d70,
0x2d7f, 0x2d96,
- 0x2da0, 0x2e31,
+ 0x2da0, 0x2e3b,
0x2e80, 0x2ef3,
0x2f00, 0x2fd5,
0x2ff0, 0x2ffb,
@@ -188,16 +188,15 @@ var isPrint16 = []uint16{
0x3131, 0x31ba,
0x31c0, 0x31e3,
0x31f0, 0x4db5,
- 0x4dc0, 0x9fcb,
+ 0x4dc0, 0x9fcc,
0xa000, 0xa48c,
0xa490, 0xa4c6,
0xa4d0, 0xa62b,
- 0xa640, 0xa673,
- 0xa67c, 0xa697,
- 0xa6a0, 0xa6f7,
- 0xa700, 0xa791,
- 0xa7a0, 0xa7a9,
- 0xa7fa, 0xa82b,
+ 0xa640, 0xa697,
+ 0xa69f, 0xa6f7,
+ 0xa700, 0xa793,
+ 0xa7a0, 0xa7aa,
+ 0xa7f8, 0xa82b,
0xa830, 0xa839,
0xa840, 0xa877,
0xa880, 0xa8c4,
@@ -212,7 +211,7 @@ var isPrint16 = []uint16{
0xaa50, 0xaa59,
0xaa5c, 0xaa7b,
0xaa80, 0xaac2,
- 0xaadb, 0xaadf,
+ 0xaadb, 0xaaf6,
0xab01, 0xab06,
0xab09, 0xab0e,
0xab11, 0xab16,
@@ -222,8 +221,7 @@ var isPrint16 = []uint16{
0xac00, 0xd7a3,
0xd7b0, 0xd7c6,
0xd7cb, 0xd7fb,
- 0xf900, 0xfa2d,
- 0xfa30, 0xfa6d,
+ 0xf900, 0xfa6d,
0xfa70, 0xfad9,
0xfb00, 0xfb06,
0xfb13, 0xfb17,
@@ -252,8 +250,11 @@ var isNotPrint16 = []uint16{
0x03a2,
0x0560,
0x0588,
+ 0x0590,
0x06dd,
0x083f,
+ 0x08a1,
+ 0x08ff,
0x0978,
0x0980,
0x0984,
@@ -275,7 +276,6 @@ var isNotPrint16 = []uint16{
0x0ab4,
0x0ac6,
0x0aca,
- 0x0af0,
0x0b04,
0x0b29,
0x0b31,
@@ -327,6 +327,7 @@ var isNotPrint16 = []uint16{
0x0f98,
0x0fbd,
0x0fcd,
+ 0x10c6,
0x1249,
0x1257,
0x1259,
@@ -351,10 +352,9 @@ var isNotPrint16 = []uint16{
0x1ff5,
0x208f,
0x2700,
- 0x27cb,
- 0x27cd,
0x2c2f,
0x2c5f,
+ 0x2d26,
0x2da7,
0x2daf,
0x2db7,
@@ -406,6 +406,8 @@ var isPrint32 = []uint32{
0x010900, 0x01091b,
0x01091f, 0x010939,
0x01093f, 0x01093f,
+ 0x010980, 0x0109b7,
+ 0x0109be, 0x0109bf,
0x010a00, 0x010a06,
0x010a0c, 0x010a33,
0x010a38, 0x010a3a,
@@ -421,11 +423,21 @@ var isPrint32 = []uint32{
0x011000, 0x01104d,
0x011052, 0x01106f,
0x011080, 0x0110c1,
+ 0x0110d0, 0x0110e8,
+ 0x0110f0, 0x0110f9,
+ 0x011100, 0x011143,
+ 0x011180, 0x0111c8,
+ 0x0111d0, 0x0111d9,
+ 0x011680, 0x0116b7,
+ 0x0116c0, 0x0116c9,
0x012000, 0x01236e,
0x012400, 0x012462,
0x012470, 0x012473,
0x013000, 0x01342e,
0x016800, 0x016a38,
+ 0x016f00, 0x016f44,
+ 0x016f50, 0x016f7e,
+ 0x016f8f, 0x016f9f,
0x01b000, 0x01b001,
0x01d000, 0x01d0f5,
0x01d100, 0x01d126,
@@ -442,13 +454,21 @@ var isPrint32 = []uint32{
0x01d54a, 0x01d6a5,
0x01d6a8, 0x01d7cb,
0x01d7ce, 0x01d7ff,
+ 0x01ee00, 0x01ee24,
+ 0x01ee27, 0x01ee3b,
+ 0x01ee42, 0x01ee42,
+ 0x01ee47, 0x01ee54,
+ 0x01ee57, 0x01ee64,
+ 0x01ee67, 0x01ee9b,
+ 0x01eea1, 0x01eebb,
+ 0x01eef0, 0x01eef1,
0x01f000, 0x01f02b,
0x01f030, 0x01f093,
0x01f0a0, 0x01f0ae,
0x01f0b1, 0x01f0be,
0x01f0c1, 0x01f0df,
0x01f100, 0x01f10a,
- 0x01f110, 0x01f169,
+ 0x01f110, 0x01f16b,
0x01f170, 0x01f19a,
0x01f1e6, 0x01f202,
0x01f210, 0x01f23a,
@@ -461,10 +481,9 @@ var isPrint32 = []uint32{
0x01f3e0, 0x01f3f0,
0x01f400, 0x01f4fc,
0x01f500, 0x01f53d,
+ 0x01f540, 0x01f543,
0x01f550, 0x01f567,
- 0x01f5fb, 0x01f625,
- 0x01f628, 0x01f62d,
- 0x01f630, 0x01f640,
+ 0x01f5fb, 0x01f640,
0x01f645, 0x01f64f,
0x01f680, 0x01f6c5,
0x01f700, 0x01f773,
@@ -489,6 +508,7 @@ var isNotPrint32 = []uint16{ // add 0x10000 to each entry
0x0a14,
0x0a18,
0x10bd,
+ 0x1135,
0xd455,
0xd49d,
0xd4ad,
@@ -502,6 +522,32 @@ var isNotPrint32 = []uint16{ // add 0x10000 to each entry
0xd53f,
0xd545,
0xd551,
+ 0xee04,
+ 0xee20,
+ 0xee23,
+ 0xee28,
+ 0xee33,
+ 0xee38,
+ 0xee3a,
+ 0xee48,
+ 0xee4a,
+ 0xee4c,
+ 0xee50,
+ 0xee53,
+ 0xee58,
+ 0xee5a,
+ 0xee5c,
+ 0xee5e,
+ 0xee60,
+ 0xee63,
+ 0xee6b,
+ 0xee73,
+ 0xee78,
+ 0xee7d,
+ 0xee7f,
+ 0xee8a,
+ 0xeea4,
+ 0xeeaa,
0xf0d0,
0xf12f,
0xf336,
@@ -509,13 +555,4 @@ var isNotPrint32 = []uint16{ // add 0x10000 to each entry
0xf43f,
0xf441,
0xf4f8,
- 0xf600,
- 0xf611,
- 0xf615,
- 0xf617,
- 0xf619,
- 0xf61b,
- 0xf61f,
- 0xf62c,
- 0xf634,
}
diff --git a/src/pkg/strconv/itoa_test.go b/src/pkg/strconv/itoa_test.go
index 1486ee214..e0213ae9a 100644
--- a/src/pkg/strconv/itoa_test.go
+++ b/src/pkg/strconv/itoa_test.go
@@ -5,7 +5,6 @@
package strconv_test
import (
- "runtime"
. "strconv"
"testing"
)
@@ -126,35 +125,6 @@ func TestUitoa(t *testing.T) {
}
}
-func numAllocations(f func()) int {
- runtime.GC()
- memstats := new(runtime.MemStats)
- runtime.ReadMemStats(memstats)
- n0 := memstats.Mallocs
- f()
- runtime.ReadMemStats(memstats)
- return int(memstats.Mallocs - n0)
-}
-
-var globalBuf [64]byte
-
-func TestAppendUintDoesntAllocate(t *testing.T) {
- n := numAllocations(func() {
- var buf [64]byte
- AppendInt(buf[:0], 123, 10)
- })
- want := 1 // TODO(bradfitz): this might be 0, once escape analysis is better
- if n != want {
- t.Errorf("with local buffer, did %d allocations, want %d", n, want)
- }
- n = numAllocations(func() {
- AppendInt(globalBuf[:0], 123, 10)
- })
- if n != 0 {
- t.Errorf("with reused buffer, did %d allocations, want 0", n)
- }
-}
-
func BenchmarkFormatInt(b *testing.B) {
for i := 0; i < b.N; i++ {
for _, test := range itob64tests {
diff --git a/src/pkg/strconv/quote.go b/src/pkg/strconv/quote.go
index 8a73f9d3b..8cbef88b5 100644
--- a/src/pkg/strconv/quote.go
+++ b/src/pkg/strconv/quote.go
@@ -139,8 +139,9 @@ func AppendQuoteRuneToASCII(dst []byte, r rune) []byte {
return append(dst, QuoteRuneToASCII(r)...)
}
-// CanBackquote returns whether the string s would be
-// a valid Go string literal if enclosed in backquotes.
+// CanBackquote reports whether the string s can be represented
+// unchanged as a single-line backquoted string without control
+// characters other than space and tab.
func CanBackquote(s string) bool {
for i := 0; i < len(s); i++ {
if (s[i] < ' ' && s[i] != '\t') || s[i] == '`' {
diff --git a/src/pkg/strconv/strconv_test.go b/src/pkg/strconv/strconv_test.go
new file mode 100644
index 000000000..c3c538926
--- /dev/null
+++ b/src/pkg/strconv/strconv_test.go
@@ -0,0 +1,52 @@
+// Copyright 2012 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 (
+ . "strconv"
+ "strings"
+ "testing"
+)
+
+var (
+ globalBuf [64]byte
+ nextToOne = "1.00000000000000011102230246251565404236316680908203125" + strings.Repeat("0", 10000) + "1"
+
+ mallocTest = []struct {
+ count int
+ desc string
+ fn func()
+ }{
+ // TODO(bradfitz): this might be 0, once escape analysis is better
+ {1, `AppendInt(localBuf[:0], 123, 10)`, func() {
+ var localBuf [64]byte
+ AppendInt(localBuf[:0], 123, 10)
+ }},
+ {0, `AppendInt(globalBuf[:0], 123, 10)`, func() { AppendInt(globalBuf[:0], 123, 10) }},
+ // TODO(bradfitz): this might be 0, once escape analysis is better
+ {1, `AppendFloat(localBuf[:0], 1.23, 'g', 5, 64)`, func() {
+ var localBuf [64]byte
+ AppendFloat(localBuf[:0], 1.23, 'g', 5, 64)
+ }},
+ {0, `AppendFloat(globalBuf[:0], 1.23, 'g', 5, 64)`, func() { AppendFloat(globalBuf[:0], 1.23, 'g', 5, 64) }},
+ {0, `ParseFloat("123.45", 64)`, func() { ParseFloat("123.45", 64) }},
+ {0, `ParseFloat("123.456789123456789", 64)`, func() { ParseFloat("123.456789123456789", 64) }},
+ {0, `ParseFloat("1.000000000000000111022302462515654042363166809082031251", 64)`, func() {
+ ParseFloat("1.000000000000000111022302462515654042363166809082031251", 64)
+ }},
+ {0, `ParseFloat("1.0000000000000001110223024625156540423631668090820312500...001", 64)`, func() {
+ ParseFloat(nextToOne, 64)
+ }},
+ }
+)
+
+func TestCountMallocs(t *testing.T) {
+ for _, mt := range mallocTest {
+ allocs := testing.AllocsPerRun(100, mt.fn)
+ if max := float64(mt.count); allocs > max {
+ t.Errorf("%s: %v allocs, want <=%v", mt.desc, allocs, max)
+ }
+ }
+}
diff --git a/src/pkg/strconv/testfp.txt b/src/pkg/strconv/testdata/testfp.txt
index 08d3c4ef0..08d3c4ef0 100644
--- a/src/pkg/strconv/testfp.txt
+++ b/src/pkg/strconv/testdata/testfp.txt