From 04faa08c070191a7c187e4ff3a9eb7ed9b6b38a6 Mon Sep 17 00:00:00 2001 From: Rob Pike Date: Thu, 15 Dec 2011 12:52:29 -0800 Subject: [PATCH] fmt: speed up floating point print, clean up some code %g down to two mallocs from four. Also a mild speedup. fmt_test.BenchmarkSprintfFloat 3016 2703 -10.38% Fixes #2557. R=rsc CC=golang-dev https://golang.org/cl/5491054 --- src/pkg/fmt/format.go | 93 +++++++++++++++++++++++-------------------- src/pkg/fmt/print.go | 8 ++-- 2 files changed, 53 insertions(+), 48 deletions(-) diff --git a/src/pkg/fmt/format.go b/src/pkg/fmt/format.go index 5f62c067f0..78d9e998b1 100644 --- a/src/pkg/fmt/format.go +++ b/src/pkg/fmt/format.go @@ -154,12 +154,17 @@ func putint(buf []byte, base, val uint64, digits string) int { return i - 1 } +var ( + trueBytes = []byte("true") + falseBytes = []byte("false") +) + // fmt_boolean formats a boolean. func (f *fmt) fmt_boolean(v bool) { if v { - f.padString("true") + f.pad(trueBytes) } else { - f.padString("false") + f.pad(falseBytes) } } @@ -283,31 +288,18 @@ func (f *fmt) fmt_s(s string) { } // fmt_sx formats a string as a hexadecimal encoding of its bytes. -func (f *fmt) fmt_sx(s string) { - t := "" +func (f *fmt) fmt_sx(s, digits string) { + // TODO: Avoid buffer by pre-padding. + var b bytes.Buffer for i := 0; i < len(s); i++ { if i > 0 && f.space { - t += " " + b.WriteByte(' ') } v := s[i] - t += string(ldigits[v>>4]) - t += string(ldigits[v&0xF]) + b.WriteByte(digits[v>>4]) + b.WriteByte(digits[v&0xF]) } - f.padString(t) -} - -// fmt_sX formats a string as an uppercase hexadecimal encoding of its bytes. -func (f *fmt) fmt_sX(s string) { - t := "" - for i := 0; i < len(s); i++ { - if i > 0 && f.space { - t += " " - } - v := s[i] - t += string(udigits[v>>4]) - t += string(udigits[v&0xF]) - } - f.padString(t) + f.pad(b.Bytes()) } // fmt_q formats a string as a double-quoted, escaped Go string constant. @@ -329,13 +321,13 @@ func (f *fmt) fmt_q(s string) { // fmt_qc formats the integer as a single-quoted, escaped Go character constant. // If the character is not valid Unicode, it will print '\ufffd'. func (f *fmt) fmt_qc(c int64) { - var quoted string + var quoted []byte if f.plus { - quoted = strconv.QuoteRuneToASCII(rune(c)) + quoted = strconv.AppendQuoteRuneToASCII(f.intbuf[0:0], rune(c)) } else { - quoted = strconv.QuoteRune(rune(c)) + quoted = strconv.AppendQuoteRune(f.intbuf[0:0], rune(c)) } - f.padString(quoted) + f.pad(quoted) } // floating-point @@ -347,57 +339,70 @@ func doPrec(f *fmt, def int) int { return def } -// Add a plus sign or space to the floating-point string representation if missing and required. -func (f *fmt) plusSpace(s string) { - if s[0] != '-' { +// formatFloat formats a float64; it is an efficient equivalent to f.pad(strconv.FormatFloat()...). +func (f *fmt) formatFloat(v float64, verb byte, prec, n int) { + // We leave one byte at the beginning of f.intbuf for a sign if needed, + // and make it a space, which we might be able to use. + f.intbuf[0] = ' ' + slice := strconv.AppendFloat(f.intbuf[0:1], v, verb, prec, n) + // Add a plus sign or space to the floating-point string representation if missing and required. + // The formatted number starts at slice[1]. + switch slice[1] { + case '-', '+': + // We're set; drop the leading space. + slice = slice[1:] + default: + // There's no sign, but we might need one. if f.plus { - s = "+" + s + slice[0] = '+' } else if f.space { - s = " " + s + // space is already there + } else { + slice = slice[1:] } } - f.padString(s) + f.pad(slice) } // fmt_e64 formats a float64 in the form -1.23e+12. -func (f *fmt) fmt_e64(v float64) { f.plusSpace(strconv.FormatFloat(v, 'e', doPrec(f, 6), 64)) } +func (f *fmt) fmt_e64(v float64) { f.formatFloat(v, 'e', doPrec(f, 6), 64) } // fmt_E64 formats a float64 in the form -1.23E+12. -func (f *fmt) fmt_E64(v float64) { f.plusSpace(strconv.FormatFloat(v, 'E', doPrec(f, 6), 64)) } +func (f *fmt) fmt_E64(v float64) { f.formatFloat(v, 'E', doPrec(f, 6), 64) } // fmt_f64 formats a float64 in the form -1.23. -func (f *fmt) fmt_f64(v float64) { f.plusSpace(strconv.FormatFloat(v, 'f', doPrec(f, 6), 64)) } +func (f *fmt) fmt_f64(v float64) { f.formatFloat(v, 'f', doPrec(f, 6), 64) } // fmt_g64 formats a float64 in the 'f' or 'e' form according to size. -func (f *fmt) fmt_g64(v float64) { f.plusSpace(strconv.FormatFloat(v, 'g', doPrec(f, -1), 64)) } +func (f *fmt) fmt_g64(v float64) { f.formatFloat(v, 'g', doPrec(f, -1), 64) } // fmt_g64 formats a float64 in the 'f' or 'E' form according to size. -func (f *fmt) fmt_G64(v float64) { f.plusSpace(strconv.FormatFloat(v, 'G', doPrec(f, -1), 64)) } +func (f *fmt) fmt_G64(v float64) { f.formatFloat(v, 'G', doPrec(f, -1), 64) } // fmt_fb64 formats a float64 in the form -123p3 (exponent is power of 2). -func (f *fmt) fmt_fb64(v float64) { f.plusSpace(strconv.FormatFloat(v, 'b', 0, 64)) } +func (f *fmt) fmt_fb64(v float64) { f.formatFloat(v, 'b', 0, 64) } // float32 // cannot defer to float64 versions // because it will get rounding wrong in corner cases. // fmt_e32 formats a float32 in the form -1.23e+12. -func (f *fmt) fmt_e32(v float32) { f.plusSpace(strconv.FormatFloat(float64(v), 'e', doPrec(f, 6), 32)) } +func (f *fmt) fmt_e32(v float32) { f.formatFloat(float64(v), 'e', doPrec(f, 6), 32) } // fmt_E32 formats a float32 in the form -1.23E+12. -func (f *fmt) fmt_E32(v float32) { f.plusSpace(strconv.FormatFloat(float64(v), 'E', doPrec(f, 6), 32)) } +func (f *fmt) fmt_E32(v float32) { f.formatFloat(float64(v), 'E', doPrec(f, 6), 32) } // fmt_f32 formats a float32 in the form -1.23. -func (f *fmt) fmt_f32(v float32) { f.plusSpace(strconv.FormatFloat(float64(v), 'f', doPrec(f, 6), 32)) } +func (f *fmt) fmt_f32(v float32) { f.formatFloat(float64(v), 'f', doPrec(f, 6), 32) } // fmt_g32 formats a float32 in the 'f' or 'e' form according to size. -func (f *fmt) fmt_g32(v float32) { f.plusSpace(strconv.FormatFloat(float64(v), 'g', doPrec(f, -1), 32)) } +func (f *fmt) fmt_g32(v float32) { f.formatFloat(float64(v), 'g', doPrec(f, -1), 32) } // fmt_G32 formats a float32 in the 'f' or 'E' form according to size. -func (f *fmt) fmt_G32(v float32) { f.plusSpace(strconv.FormatFloat(float64(v), 'G', doPrec(f, -1), 32)) } +func (f *fmt) fmt_G32(v float32) { f.formatFloat(float64(v), 'G', doPrec(f, -1), 32) } // fmt_fb32 formats a float32 in the form -123p3 (exponent is power of 2). -func (f *fmt) fmt_fb32(v float32) { f.padString(strconv.FormatFloat(float64(v), 'b', 0, 32)) } +func (f *fmt) fmt_fb32(v float32) { f.formatFloat(float64(v), 'b', 0, 32) } // fmt_c64 formats a complex64 according to the verb. func (f *fmt) fmt_c64(v complex64, verb rune) { diff --git a/src/pkg/fmt/print.go b/src/pkg/fmt/print.go index 9f157daaee..3b7d3464e2 100644 --- a/src/pkg/fmt/print.go +++ b/src/pkg/fmt/print.go @@ -503,9 +503,9 @@ func (p *pp) fmtString(v string, verb rune, goSyntax bool) { case 's': p.fmt.fmt_s(v) case 'x': - p.fmt.fmt_sx(v) + p.fmt.fmt_sx(v, ldigits) case 'X': - p.fmt.fmt_sX(v) + p.fmt.fmt_sx(v, udigits) case 'q': p.fmt.fmt_q(v) default: @@ -542,9 +542,9 @@ func (p *pp) fmtBytes(v []byte, verb rune, goSyntax bool, depth int) { case 's': p.fmt.fmt_s(s) case 'x': - p.fmt.fmt_sx(s) + p.fmt.fmt_sx(s, ldigits) case 'X': - p.fmt.fmt_sX(s) + p.fmt.fmt_sx(s, udigits) case 'q': p.fmt.fmt_q(s) default: -- 2.48.1