summaryrefslogtreecommitdiff
path: root/src/fmt
diff options
context:
space:
mode:
authorMartin Möhrmann <martisch@uos.de>2016-02-27 14:37:10 +0100
committerRob Pike <r@golang.org>2016-03-06 02:18:05 +0000
commitbd6d84282a441a35b4629eaa958d3ce24d396a6f (patch)
treeda6356cbc6b10c793edf5f660469c73675c03a19 /src/fmt
parent5763476f739ad4d6b122c2a7ae26a845ea020a7e (diff)
downloadgo-git-bd6d84282a441a35b4629eaa958d3ce24d396a6f.tar.gz
fmt: refactor and unify float and complex formatting
Removes specialized functions for each verb and float/complex size and replaces them with generic variants fmtFloat and fmtComplex similar to other generic fmt functions. Simplifies the complex formatting by relying on fmtFloat to handle the verb and default precision selection. Complex imaginary formatting does not need to clear the f.space flag because the set f.plus flag will force a sign instead of a space. Sets default precision for %b to -1 (same as %g and %G) since precision for %b has no affect in strconv.AppendFloat. Add more tests and group them a bit better. Use local copies of +Inf,-Inf and NaN instead of math package functions for testing. Saves around 8kb in the go binary. name old time/op new time/op delta SprintfFloat-2 200ns ± 4% 196ns ± 4% -1.55% (p=0.007 n=20+20) SprintfComplex-2 569ns ± 4% 570ns ± 3% ~ (p=0.804 n=20+20) Change-Id: I36d35dab6f835fc2bd2c042ac97705868eb2446f Reviewed-on: https://go-review.googlesource.com/20252 Reviewed-by: Rob Pike <r@golang.org> Run-TryBot: Rob Pike <r@golang.org>
Diffstat (limited to 'src/fmt')
-rw-r--r--src/fmt/fmt_test.go100
-rw-r--r--src/fmt/format.go99
-rw-r--r--src/fmt/print.go100
3 files changed, 115 insertions, 184 deletions
diff --git a/src/fmt/fmt_test.go b/src/fmt/fmt_test.go
index 8287d305af..2ff3515c52 100644
--- a/src/fmt/fmt_test.go
+++ b/src/fmt/fmt_test.go
@@ -48,6 +48,12 @@ func TestFmtInterface(t *testing.T) {
}
}
+var (
+ NaN = math.NaN()
+ posInf = math.Inf(1)
+ negInf = math.Inf(-1)
+)
+
const b32 uint32 = 1<<32 - 1
const b64 uint64 = 1<<64 - 1
@@ -345,9 +351,31 @@ var fmtTests = []struct {
{"% .3g", 1.0, " 1"},
{"%b", float32(1.0), "8388608p-23"},
{"%b", 1.0, "4503599627370496p-52"},
+ // Precision has no effect for binary float format.
+ {"%.4b", float32(1.0), "8388608p-23"},
+ {"%.4b", -1.0, "-4503599627370496p-52"},
+ // float infinites and NaNs
+ {"%f", posInf, "+Inf"},
+ {"%.1f", negInf, "-Inf"},
+ {"% f", NaN, " NaN"},
+ {"%20f", posInf, " +Inf"},
+ {"% 20F", posInf, " Inf"},
+ {"% 20e", negInf, " -Inf"},
+ {"%+20E", negInf, " -Inf"},
+ {"% +20g", negInf, " -Inf"},
+ {"%+-20G", posInf, "+Inf "},
+ {"%20e", NaN, " NaN"},
+ {"% +20E", NaN, " +NaN"},
+ {"% -20g", NaN, " NaN "},
+ {"%+-20G", NaN, "+NaN "},
+ // Zero padding does not apply to infinities and NaN.
+ {"%+020e", posInf, " +Inf"},
+ {"%-020f", negInf, "-Inf "},
+ {"%-020E", NaN, "NaN "},
// complex values
{"%.f", 0i, "(0+0i)"},
+ {"% .f", 0i, "( 0+0i)"},
{"%+.f", 0i, "(+0+0i)"},
{"% +.f", 0i, "(+0+0i)"},
{"%+.3e", 0i, "(+0.000e+00+0.000e+00i)"},
@@ -368,10 +396,31 @@ var fmtTests = []struct {
{"%.3f", -1 - 2i, "(-1.000-2.000i)"},
{"%.3g", -1 - 2i, "(-1-2i)"},
{"% .3E", -1 - 2i, "(-1.000E+00-2.000E+00i)"},
+ {"%+.3g", 1 + 2i, "(+1+2i)"},
{"%+.3g", complex64(1 + 2i), "(+1+2i)"},
- {"%+.3g", complex128(1 + 2i), "(+1+2i)"},
- {"%b", complex64(1 + 2i), "(8388608p-23+8388608p-22i)"},
{"%b", 1 + 2i, "(4503599627370496p-52+4503599627370496p-51i)"},
+ {"%b", complex64(1 + 2i), "(8388608p-23+8388608p-22i)"},
+ // Precision has no effect for binary complex format.
+ {"%.4b", 1 + 2i, "(4503599627370496p-52+4503599627370496p-51i)"},
+ {"%.4b", complex64(1 + 2i), "(8388608p-23+8388608p-22i)"},
+ // complex infinites and NaNs
+ {"%f", complex(posInf, posInf), "(+Inf+Infi)"},
+ {"%f", complex(negInf, negInf), "(-Inf-Infi)"},
+ {"%f", complex(NaN, NaN), "(NaN+NaNi)"},
+ {"%.1f", complex(posInf, posInf), "(+Inf+Infi)"},
+ {"% f", complex(posInf, posInf), "( Inf+Infi)"},
+ {"% f", complex(negInf, negInf), "(-Inf-Infi)"},
+ {"% f", complex(NaN, NaN), "( NaN+NaNi)"},
+ {"%8e", complex(posInf, posInf), "( +Inf +Infi)"},
+ {"% 8E", complex(posInf, posInf), "( Inf +Infi)"},
+ {"%+8f", complex(negInf, negInf), "( -Inf -Infi)"},
+ {"% +8g", complex(negInf, negInf), "( -Inf -Infi)"},
+ {"% -8G", complex(NaN, NaN), "( NaN +NaN i)"},
+ {"%+-8b", complex(NaN, NaN), "(+NaN +NaN i)"},
+ // Zero padding does not apply to infinities and NaN.
+ {"%08f", complex(posInf, posInf), "( +Inf +Infi)"},
+ {"%-08g", complex(negInf, negInf), "(-Inf -Inf i)"},
+ {"%-08G", complex(NaN, NaN), "(NaN +NaN i)"},
// erroneous formats
{"", 2, "%!(EXTRA int=2)"},
@@ -455,16 +504,6 @@ var fmtTests = []struct {
{"%g", 1.23456789e3, "1234.56789"},
{"%g", 1.23456789e-3, "0.00123456789"},
{"%g", 1.23456789e20, "1.23456789e+20"},
- {"%20e", math.Inf(1), " +Inf"},
- {"% 20f", math.Inf(1), " Inf"},
- {"%+20f", math.Inf(1), " +Inf"},
- {"% +20f", math.Inf(1), " +Inf"},
- {"%-20f", math.Inf(-1), "-Inf "},
- {"%20g", math.NaN(), " NaN"},
- {"%+20f", math.NaN(), " +NaN"},
- {"% +20f", math.NaN(), " +NaN"},
- {"% -20f", math.NaN(), " NaN "},
- {"%+-20f", math.NaN(), "+NaN "},
// arrays
{"%v", array, "[1 2 3 4 5]"},
@@ -533,10 +572,13 @@ var fmtTests = []struct {
{"%# -6d", []byte{1, 11, 111}, "[ 1 11 111 ]"},
{"%#+-6d", [3]byte{1, 11, 111}, "[+1 +11 +111 ]"},
+ // floates with %v
+ {"%v", 1.2345678, "1.2345678"},
+ {"%v", float32(1.2345678), "1.2345678"},
+
// complexes with %v
{"%v", 1 + 2i, "(1+2i)"},
{"%v", complex64(1 + 2i), "(1+2i)"},
- {"%v", complex128(1 + 2i), "(1+2i)"},
// structs
{"%v", A{1, 2, "a", []int{1, 2}}, `{1 2 a [1 2]}`},
@@ -578,6 +620,8 @@ var fmtTests = []struct {
{"%#v", bslice, `[]fmt_test.renamedUint8{0x1, 0x2, 0x3, 0x4, 0x5}`},
{"%#v", []byte(nil), "[]byte(nil)"},
{"%#v", []int32(nil), "[]int32(nil)"},
+ {"%#v", 1.2345678, "1.2345678"},
+ {"%#v", float32(1.2345678), "1.2345678"},
// slices with other formats
{"%#x", []int{1, 2, 15}, `[0x1 0x2 0xf]`},
@@ -732,7 +776,7 @@ var fmtTests = []struct {
// be fetched directly, the lookup fails and returns a
// zero reflect.Value, which formats as <nil>.
// This test is just to check that it shows the two NaNs at all.
- {"%v", map[float64]int{math.NaN(): 1, math.NaN(): 2}, "map[NaN:<nil> NaN:<nil>]"},
+ {"%v", map[float64]int{NaN: 1, NaN: 2}, "map[NaN:<nil> NaN:<nil>]"},
// Used to crash because nByte didn't allow for a sign.
{"%b", int64(-1 << 63), zeroFill("-1", 63, "")},
@@ -822,19 +866,7 @@ var fmtTests = []struct {
// Complex numbers: exhaustively tested in TestComplexFormatting.
{"%7.2f", 1 + 2i, "( 1.00 +2.00i)"},
{"%+07.2f", -1 - 2i, "(-001.00-002.00i)"},
- // Zero padding does not apply to infinities and NaN.
- {"%020f", math.Inf(-1), " -Inf"},
- {"%020f", math.Inf(+1), " +Inf"},
- {"%020f", math.NaN(), " NaN"},
- {"% 020f", math.Inf(-1), " -Inf"},
- {"% 020f", math.Inf(+1), " Inf"},
- {"% 020f", math.NaN(), " NaN"},
- {"%+020f", math.Inf(-1), " -Inf"},
- {"%+020f", math.Inf(+1), " +Inf"},
- {"%+020f", math.NaN(), " +NaN"},
- {"%-020f", math.Inf(-1), "-Inf "},
- {"%-020f", math.Inf(+1), "+Inf "},
- {"%-020f", math.NaN(), "NaN "},
+
{"%20f", -1.0, " -1.000000"},
// Make sure we can handle very large widths.
{"%0100f", -1.0, zeroFill("-", 99, "1.000000")},
@@ -925,6 +957,10 @@ var fmtTests = []struct {
{"%☠", []uint8{0}, "%!☠([]uint8=[0])"},
{"%☠", [1]byte{0}, "%!☠([1]uint8=[0])"},
{"%☠", [1]uint8{0}, "%!☠([1]uint8=[0])"},
+ {"%☠", 1.2345678, "%!☠(float64=1.2345678)"},
+ {"%☠", float32(1.2345678), "%!☠(float32=1.2345678)"},
+ {"%☠", 1.2345678 + 1.2345678i, "%!☠(complex128=(1.2345678+1.2345678i))"},
+ {"%☠", complex64(1.2345678 + 1.2345678i), "%!☠(complex64=(1.2345678+1.2345678i))"},
}
// zeroFill generates zero-filled strings of the specified width. The length
@@ -974,7 +1010,7 @@ func TestSprintf(t *testing.T) {
// thing as if done by hand with two singleton prints.
func TestComplexFormatting(t *testing.T) {
var yesNo = []bool{true, false}
- var values = []float64{1, 0, -1, math.Inf(1), math.Inf(-1), math.NaN()}
+ var values = []float64{1, 0, -1, posInf, negInf, NaN}
for _, plus := range yesNo {
for _, zero := range yesNo {
for _, space := range yesNo {
@@ -1140,6 +1176,14 @@ func BenchmarkSprintfFloat(b *testing.B) {
})
}
+func BenchmarkSprintfComplex(b *testing.B) {
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ Sprintf("%f", 5.23184+5.23184i)
+ }
+ })
+}
+
func BenchmarkSprintfBoolean(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
diff --git a/src/fmt/format.go b/src/fmt/format.go
index 417d74cd8f..703d8a647c 100644
--- a/src/fmt/format.go
+++ b/src/fmt/format.go
@@ -405,19 +405,15 @@ func (f *fmt) fmt_qc(c int64) {
}
}
-// floating-point
-
-func doPrec(f *fmt, def int) int {
+// fmt_float formats a float64. It assumes that verb is a valid format specifier
+// for strconv.AppendFloat and therefore fits into a byte.
+func (f *fmt) fmt_float(v float64, size int, verb rune, prec int) {
+ // Explicit precision in format specifier overrules default precision.
if f.precPresent {
- return f.prec
+ prec = f.prec
}
- return def
-}
-
-// 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) {
// Format number, reserving space for leading + sign if needed.
- num := strconv.AppendFloat(f.intbuf[:1], v, verb, prec, n)
+ num := strconv.AppendFloat(f.intbuf[:1], v, byte(verb), prec, size)
if num[1] == '-' || num[1] == '+' {
num = num[1:]
} else {
@@ -458,86 +454,3 @@ func (f *fmt) formatFloat(v float64, verb byte, prec, n int) {
// No sign to show and the number is positive; just print the unsigned number.
f.pad(num[1:])
}
-
-// fmt_e64 formats a float64 in the form -1.23e+12.
-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.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.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.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.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.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.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.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.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.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.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.formatFloat(float64(v), 'b', 0, 32) }
-
-// fmt_c64 formats a complex64 according to the verb.
-func (f *fmt) fmt_c64(v complex64, verb rune) {
- f.fmt_complex(float64(real(v)), float64(imag(v)), 32, verb)
-}
-
-// fmt_c128 formats a complex128 according to the verb.
-func (f *fmt) fmt_c128(v complex128, verb rune) {
- f.fmt_complex(real(v), imag(v), 64, verb)
-}
-
-// fmt_complex formats a complex number as (r+ji).
-func (f *fmt) fmt_complex(r, j float64, size int, verb rune) {
- f.buf.WriteByte('(')
- oldPlus := f.plus
- oldSpace := f.space
- for i := 0; ; i++ {
- switch verb {
- case 'b':
- f.formatFloat(r, 'b', 0, size)
- case 'e':
- f.formatFloat(r, 'e', doPrec(f, 6), size)
- case 'E':
- f.formatFloat(r, 'E', doPrec(f, 6), size)
- case 'f', 'F':
- f.formatFloat(r, 'f', doPrec(f, 6), size)
- case 'g':
- f.formatFloat(r, 'g', doPrec(f, -1), size)
- case 'G':
- f.formatFloat(r, 'G', doPrec(f, -1), size)
- }
- if i != 0 {
- break
- }
- // Imaginary part always has a sign.
- f.plus = true
- f.space = false
- r = j
- }
- f.space = oldSpace
- f.plus = oldPlus
- f.buf.WriteString("i)")
-}
diff --git a/src/fmt/print.go b/src/fmt/print.go
index 5fe34c87f6..9fb33b25a4 100644
--- a/src/fmt/print.go
+++ b/src/fmt/print.go
@@ -441,61 +441,39 @@ func (p *pp) fmtUint64(v uint64, verb rune) {
}
}
-func (p *pp) fmtFloat32(v float32, verb rune) {
+// fmtFloat formats a float. The default precision for each verb
+// is specified as last argument in the call to fmt_float.
+func (p *pp) fmtFloat(v float64, size int, verb rune) {
switch verb {
- case 'b':
- p.fmt.fmt_fb32(v)
- case 'e':
- p.fmt.fmt_e32(v)
- case 'E':
- p.fmt.fmt_E32(v)
- case 'f', 'F':
- p.fmt.fmt_f32(v)
- case 'g', 'v':
- p.fmt.fmt_g32(v)
- case 'G':
- p.fmt.fmt_G32(v)
- default:
- p.badVerb(verb)
- }
-}
-
-func (p *pp) fmtFloat64(v float64, verb rune) {
- switch verb {
- case 'b':
- p.fmt.fmt_fb64(v)
- case 'e':
- p.fmt.fmt_e64(v)
- case 'E':
- p.fmt.fmt_E64(v)
- case 'f', 'F':
- p.fmt.fmt_f64(v)
- case 'g', 'v':
- p.fmt.fmt_g64(v)
- case 'G':
- p.fmt.fmt_G64(v)
- default:
- p.badVerb(verb)
- }
-}
-
-func (p *pp) fmtComplex64(v complex64, verb rune) {
- switch verb {
- case 'b', 'e', 'E', 'f', 'F', 'g', 'G':
- p.fmt.fmt_c64(v, verb)
case 'v':
- p.fmt.fmt_c64(v, 'g')
+ p.fmt.fmt_float(v, size, 'g', -1)
+ case 'b', 'g', 'G':
+ p.fmt.fmt_float(v, size, verb, -1)
+ case 'f', 'e', 'E':
+ p.fmt.fmt_float(v, size, verb, 6)
+ case 'F':
+ p.fmt.fmt_float(v, size, 'f', 6)
default:
p.badVerb(verb)
}
}
-func (p *pp) fmtComplex128(v complex128, verb rune) {
+// fmtComplex formats a complex number v with
+// r = real(v) and j = imag(v) as (r+ji) using
+// fmtFloat for r and j formatting.
+func (p *pp) fmtComplex(v complex128, size int, verb rune) {
+ // Make sure any unsupported verbs are found before the
+ // calls to fmtFloat to not generate an incorrect error string.
switch verb {
- case 'b', 'e', 'E', 'f', 'F', 'g', 'G':
- p.fmt.fmt_c128(v, verb)
- case 'v':
- p.fmt.fmt_c128(v, 'g')
+ case 'v', 'b', 'g', 'G', 'f', 'F', 'e', 'E':
+ oldPlus := p.fmt.plus
+ p.buf.WriteByte('(')
+ p.fmtFloat(real(v), size/2, verb)
+ // Imaginary part always has a sign.
+ p.fmt.plus = true
+ p.fmtFloat(imag(v), size/2, verb)
+ p.buf.WriteString("i)")
+ p.fmt.plus = oldPlus
default:
p.badVerb(verb)
}
@@ -744,13 +722,13 @@ func (p *pp) printArg(arg interface{}, verb rune, depth int) {
case bool:
p.fmtBool(f, verb)
case float32:
- p.fmtFloat32(f, verb)
+ p.fmtFloat(float64(f), 32, verb)
case float64:
- p.fmtFloat64(f, verb)
+ p.fmtFloat(f, 64, verb)
case complex64:
- p.fmtComplex64(f, verb)
+ p.fmtComplex(complex128(f), 64, verb)
case complex128:
- p.fmtComplex128(f, verb)
+ p.fmtComplex(f, 128, verb)
case int:
p.fmtInt64(int64(f), verb)
case int8:
@@ -845,18 +823,14 @@ BigSwitch:
p.fmtInt64(f.Int(), verb)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
p.fmtUint64(f.Uint(), verb)
- case reflect.Float32, reflect.Float64:
- if f.Type().Size() == 4 {
- p.fmtFloat32(float32(f.Float()), verb)
- } else {
- p.fmtFloat64(f.Float(), verb)
- }
- case reflect.Complex64, reflect.Complex128:
- if f.Type().Size() == 8 {
- p.fmtComplex64(complex64(f.Complex()), verb)
- } else {
- p.fmtComplex128(f.Complex(), verb)
- }
+ case reflect.Float32:
+ p.fmtFloat(f.Float(), 32, verb)
+ case reflect.Float64:
+ p.fmtFloat(f.Float(), 64, verb)
+ case reflect.Complex64:
+ p.fmtComplex(f.Complex(), 64, verb)
+ case reflect.Complex128:
+ p.fmtComplex(f.Complex(), 128, verb)
case reflect.String:
p.fmtString(f.String(), verb)
case reflect.Map: