From 431e51b8a00734f4e1a7255b29797ed50f8a1054 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Sun, 27 Sep 2020 14:26:09 +0200 Subject: [PATCH] runtime: use dedicated printfloat32 It can be unexpected that printing a float32 involves 64-bit floating point routines, see for example: https://github.com/tinygo-org/tinygo/issues/1415 This commit adds a dedicated printfloat32 instead just for printing float32 values. It comes with a possible code size increase, but only if both float32 and float64 values are printed. Therefore, this should be an improvement in almost all cases. I also tried using printfloat32 for everything (and casting a float64 to float32 to print) but the printed values are slightly different, breaking the testdata/math.go test for example. --- src/runtime/print.go | 86 ++++++++++++++++++++++++++++++++++++++++++-- testdata/print.go | 3 ++ testdata/print.txt | 1 + 3 files changed, 87 insertions(+), 3 deletions(-) diff --git a/src/runtime/print.go b/src/runtime/print.go index 706904fb..f7d3a285 100644 --- a/src/runtime/print.go +++ b/src/runtime/print.go @@ -98,10 +98,90 @@ func printint64(n int64) { printuint64(uint64(n)) } +// printfloat32() was copied from the relevant source in the original Go +// implementation and modified to work with float32 instead of float64. It is +// copyright by the Go authors, licensed under the same BSD 3-clause license. +// See https://golang.org/LICENSE for details. +// +// It is a near-duplicate of printfloat64. This is done so that printing a +// float32 value doesn't involve float64 routines, which can be unexpected and a +// problem sometimes. It comes with a possible code size reduction if both +// printfloat32 and printfloat64 are used, which seems uncommon. +// +// Source: +// https://github.com/golang/go/blob/master/src/runtime/print.go func printfloat32(v float32) { - // TODO: write an implementation like printfloat64, as some systems have - // 32-bit floats but only software emulation for 64-bit floats. - printfloat64(float64(v)) + switch { + case v != v: + printstring("NaN") + return + case v+v == v && v > 0: + printstring("+Inf") + return + case v+v == v && v < 0: + printstring("-Inf") + return + } + + const n = 7 // digits printed + var buf [n + 7]byte + buf[0] = '+' + e := 0 // exp + if v == 0 { + if 1/v < 0 { + buf[0] = '-' + } + } else { + if v < 0 { + v = -v + buf[0] = '-' + } + + // normalize + for v >= 10 { + e++ + v /= 10 + } + for v < 1 { + e-- + v *= 10 + } + + // round + h := float32(5.0) + for i := 0; i < n; i++ { + h /= 10 + } + v += h + if v >= 10 { + e++ + v /= 10 + } + } + + // format +d.dddd+edd + for i := 0; i < n; i++ { + s := int(v) + buf[i+2] = byte(s + '0') + v -= float32(s) + v *= 10 + } + buf[1] = buf[2] + buf[2] = '.' + + buf[n+2] = 'e' + buf[n+3] = '+' + if e < 0 { + e = -e + buf[n+3] = '-' + } + + buf[n+4] = byte(e/100) + '0' + buf[n+5] = byte(e/10)%10 + '0' + buf[n+6] = byte(e%10) + '0' + for _, c := range buf { + putchar(c) + } } // printfloat64() was copied from the relevant source in the original Go diff --git a/testdata/print.go b/testdata/print.go index df44647c..2b5f0a09 100644 --- a/testdata/print.go +++ b/testdata/print.go @@ -29,6 +29,9 @@ func main() { // print float64 println(3.14) + // print float32 + println(float32(3.14)) + // print complex128 println(5 + 1.2345i) diff --git a/testdata/print.txt b/testdata/print.txt index fcffe718..372eda95 100644 --- a/testdata/print.txt +++ b/testdata/print.txt @@ -16,6 +16,7 @@ a b c 123456789012 -123456789012 +3.140000e+000 ++3.140000e+000 (+5.000000e+000+1.234500e+000i) (0:nil) map[2]