
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.
382 строки
6,1 КиБ
Go
382 строки
6,1 КиБ
Go
package runtime
|
|
|
|
import (
|
|
"unsafe"
|
|
)
|
|
|
|
type stringer interface {
|
|
String() string
|
|
}
|
|
|
|
//go:nobounds
|
|
func printstring(s string) {
|
|
for i := 0; i < len(s); i++ {
|
|
putchar(s[i])
|
|
}
|
|
}
|
|
|
|
func printuint8(n uint8) {
|
|
if TargetBits >= 32 {
|
|
printuint32(uint32(n))
|
|
} else {
|
|
prevdigits := n / 10
|
|
if prevdigits != 0 {
|
|
printuint8(prevdigits)
|
|
}
|
|
putchar(byte((n % 10) + '0'))
|
|
}
|
|
}
|
|
|
|
func printint8(n int8) {
|
|
if TargetBits >= 32 {
|
|
printint32(int32(n))
|
|
} else {
|
|
if n < 0 {
|
|
putchar('-')
|
|
n = -n
|
|
}
|
|
printuint8(uint8(n))
|
|
}
|
|
}
|
|
|
|
func printuint16(n uint16) {
|
|
printuint32(uint32(n))
|
|
}
|
|
|
|
func printint16(n int16) {
|
|
printint32(int32(n))
|
|
}
|
|
|
|
func printuint32(n uint32) {
|
|
if TargetBits == 8 {
|
|
// AVR-specific workaround on LLVM 10. Should be removed when we switch
|
|
// to LLVM 11.
|
|
prevdigits := n / 10
|
|
if prevdigits != 0 {
|
|
printuint32(prevdigits)
|
|
}
|
|
putchar(byte((n % 10) + '0'))
|
|
return
|
|
}
|
|
printuint64(uint64(n))
|
|
}
|
|
|
|
func printint32(n int32) {
|
|
// Print integer in signed big-endian base-10 notation, for humans to
|
|
// read.
|
|
if n < 0 {
|
|
putchar('-')
|
|
n = -n
|
|
}
|
|
printuint32(uint32(n))
|
|
}
|
|
|
|
//go:nobounds
|
|
func printuint64(n uint64) {
|
|
digits := [20]byte{} // enough to hold (2^64)-1
|
|
// Fill in all 10 digits.
|
|
firstdigit := 19 // digit index that isn't zero (by default, the last to handle '0' correctly)
|
|
for i := 19; i >= 0; i-- {
|
|
digit := byte(n%10 + '0')
|
|
digits[i] = digit
|
|
if digit != '0' {
|
|
firstdigit = i
|
|
}
|
|
n /= 10
|
|
}
|
|
// Print digits without the leading zeroes.
|
|
for i := firstdigit; i < 20; i++ {
|
|
putchar(digits[i])
|
|
}
|
|
}
|
|
|
|
func printint64(n int64) {
|
|
if n < 0 {
|
|
putchar('-')
|
|
n = -n
|
|
}
|
|
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) {
|
|
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
|
|
// implementation. It is copyright by the Go authors, licensed under the same
|
|
// BSD 3-clause license. See https://golang.org/LICENSE for details.
|
|
//
|
|
// Source:
|
|
// https://github.com/golang/go/blob/master/src/runtime/print.go
|
|
func printfloat64(v float64) {
|
|
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 := 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 -= float64(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)
|
|
}
|
|
}
|
|
|
|
func printcomplex64(c complex64) {
|
|
putchar('(')
|
|
printfloat32(real(c))
|
|
printfloat32(imag(c))
|
|
printstring("i)")
|
|
}
|
|
|
|
func printcomplex128(c complex128) {
|
|
putchar('(')
|
|
printfloat64(real(c))
|
|
printfloat64(imag(c))
|
|
printstring("i)")
|
|
}
|
|
|
|
func printspace() {
|
|
putchar(' ')
|
|
}
|
|
|
|
func printnl() {
|
|
putchar('\r')
|
|
putchar('\n')
|
|
}
|
|
|
|
func printitf(msg interface{}) {
|
|
switch msg := msg.(type) {
|
|
case bool:
|
|
print(msg)
|
|
case int:
|
|
print(msg)
|
|
case int8:
|
|
print(msg)
|
|
case int16:
|
|
print(msg)
|
|
case int32:
|
|
print(msg)
|
|
case int64:
|
|
print(msg)
|
|
case uint:
|
|
print(msg)
|
|
case uint8:
|
|
print(msg)
|
|
case uint16:
|
|
print(msg)
|
|
case uint32:
|
|
print(msg)
|
|
case uint64:
|
|
print(msg)
|
|
case uintptr:
|
|
print(msg)
|
|
case float32:
|
|
print(msg)
|
|
case float64:
|
|
print(msg)
|
|
case complex64:
|
|
print(msg)
|
|
case complex128:
|
|
print(msg)
|
|
case string:
|
|
print(msg)
|
|
case error:
|
|
print(msg.Error())
|
|
case stringer:
|
|
print(msg.String())
|
|
default:
|
|
// cast to underlying type
|
|
itf := *(*_interface)(unsafe.Pointer(&msg))
|
|
putchar('(')
|
|
switch unsafe.Sizeof(itf.typecode) {
|
|
case 2:
|
|
printuint16(uint16(itf.typecode))
|
|
case 4:
|
|
printuint32(uint32(itf.typecode))
|
|
case 8:
|
|
printuint64(uint64(itf.typecode))
|
|
}
|
|
putchar(':')
|
|
print(itf.value)
|
|
putchar(')')
|
|
}
|
|
}
|
|
|
|
func printmap(m *hashmap) {
|
|
print("map[")
|
|
if m == nil {
|
|
print("nil")
|
|
} else {
|
|
print(uint(m.count))
|
|
}
|
|
putchar(']')
|
|
}
|
|
|
|
func printptr(ptr uintptr) {
|
|
if ptr == 0 {
|
|
print("nil")
|
|
return
|
|
}
|
|
putchar('0')
|
|
putchar('x')
|
|
for i := 0; i < int(unsafe.Sizeof(ptr))*2; i++ {
|
|
nibble := byte(ptr >> (unsafe.Sizeof(ptr)*8 - 4))
|
|
if nibble < 10 {
|
|
putchar(nibble + '0')
|
|
} else {
|
|
putchar(nibble - 10 + 'a')
|
|
}
|
|
ptr <<= 4
|
|
}
|
|
}
|
|
|
|
func printbool(b bool) {
|
|
if b {
|
|
printstring("true")
|
|
} else {
|
|
printstring("false")
|
|
}
|
|
}
|