diff --git a/src/lib/fmt/Makefile b/src/lib/fmt/Makefile index 64ca60c91f..b9148ccf04 100644 --- a/src/lib/fmt/Makefile +++ b/src/lib/fmt/Makefile @@ -10,15 +10,17 @@ CC=$(O)c -w AS=$(O)a AR=$(O)ar -PKG=$(GOROOT)/pkg/fmt.a +PKG=fmt.a +PKGDIR=$(GOROOT)/pkg install: $(PKG) + mv $(PKG) $(PKGDIR)/$(PKG) nuke: clean - rm -f $(PKG) + rm -f $(PKGDIR)/$(PKG) clean: - rm -f *.$O *.a + rm -f *.$O *.a $(PKG) %.$O: %.go $(GC) $*.go @@ -39,8 +41,10 @@ O2=\ $(PKG): a1 a2 a1: $(O1) $(AR) grc $(PKG) $(O1) + rm -f $(O1) a2: $(O2) $(AR) grc $(PKG) $(O2) + rm -f $(O2) $(O1): nuke $(O2): a1 diff --git a/src/lib/fmt/format.go b/src/lib/fmt/format.go index 089de43f40..058c619443 100644 --- a/src/lib/fmt/format.go +++ b/src/lib/fmt/format.go @@ -4,6 +4,8 @@ package fmt +import "strconv" + /* Raw formatter. See print.go for a more palatable interface. @@ -181,7 +183,7 @@ func (f *Fmt) d64(a int64) *Fmt { f.clearflags(); return f; } - + func (f *Fmt) d32(a int32) *Fmt { return f.d64(int64(a)); } @@ -332,227 +334,82 @@ func (f *Fmt) s(s string) *Fmt { return f; } -func pow10(n int) float64 { - var d float64; +// floating-point - neg := false; - if n < 0 { - if n < -307 { // DBL_MIN_10_EXP - return 0.; - } - neg = true; - n = -n; - }else if n > 308 { // DBL_MAX_10_EXP - return 1.79769e+308; // HUGE_VAL +func Prec(f *Fmt, def int) int { + if f.prec_present { + return f.prec; } - - if n < NPows10 { - d = pows10[n]; - } else { - d = pows10[NPows10-1]; - for { - n -= NPows10 - 1; - if n < NPows10 { - d *= pows10[n]; - break; - } - d *= pows10[NPows10 - 1]; - } - } - if neg { - return 1/d; - } - return d; + return def; } -func unpack(a float64) (negative bool, exp int, num float64) { - if a == 0 { - return false, 0, 0.0 - } - neg := a < 0; - if neg { - a = -a; - } - // find g,e such that a = g*10^e. - // guess 10-exponent using 2-exponent, then fine tune. - g, e2 := sys.frexp(a); - e := int(float64(e2) * .301029995663981); - g = a * pow10(-e); - for g < 1 { - e--; - g = a * pow10(-e); - } - for g >= 10 { - e++; - g = a * pow10(-e); - } - return neg, e, g; -} - -// check for Inf, NaN -func(f *Fmt) InfOrNan(a float64) bool { - if sys.isInf(a, 0) { - if sys.isInf(a, 1) { - f.pad("Inf"); - } else { - f.pad("-Inf"); - } - f.clearflags(); - return true; - } - if sys.isNaN(a) { - f.pad("NaN"); - f.clearflags(); - return true; - } - return false; +func FmtString(f *Fmt, s string) *Fmt { + f.pad(s); + f.clearflags(); + return f; } // float64 func (f *Fmt) e64(a float64) *Fmt { - var negative bool; - var g float64; - var exp int; - if f.InfOrNan(a) { - return f; - } - negative, exp, g = unpack(a); - prec := 6; - if f.prec_present { - prec = f.prec; - } - prec++; // one digit left of decimal - var s string; - // multiply by 10^prec to get decimal places; put decimal after first digit - if g == 0 { - // doesn't work for zero - fake it - s = "000000000000000000000000000000000000000000000000000000000000"; - if prec < len(s) { - s = s[0:prec]; - } else { - prec = len(s); - } - } else { - g *= pow10(prec); - s = f.integer(int64(g + .5), 10, true, &ldigits); // get the digits into a string - } - s = s[0:1] + "." + s[1:prec]; // insert a decimal point - // print exponent with leading 0 if appropriate. - es := New().p(2).integer(int64(exp), 10, true, &ldigits); - if exp >= 0 { - es = "+" + es; // TODO: should do this with a fmt flag - } - s = s + "e" + es; - if negative { - s = "-" + s; - } - f.pad(s); - f.clearflags(); - return f; + return FmtString(f, strconv.ftoa64(a, 'e', Prec(f, 6))); } -// float64 func (f *Fmt) f64(a float64) *Fmt { - var negative bool; - var g float64; - var exp int; - if f.InfOrNan(a) { - return f; - } - negative, exp, g = unpack(a); - if exp > 19 || exp < -19 { // too big for this sloppy code - return f.e64(a); - } - prec := 6; - if f.prec_present { - prec = f.prec; - } - // prec is number of digits after decimal point - s := "NO"; - if exp >= 0 { - g *= pow10(exp); - gi := int64(g); - s = New().integer(gi, 10, true, &ldigits); - s = s + "."; - g -= float64(gi); - s = s + New().p(prec).integer(int64(g*pow10(prec) + .5), 10, true, &ldigits); - } else { - g *= pow10(prec + exp); - s = "0." + New().p(prec).integer(int64(g + .5), 10, true, &ldigits); - } - if negative { - s = "-" + s; - } - f.pad(s); - f.clearflags(); - return f; + return FmtString(f, strconv.ftoa64(a, 'f', Prec(f, 6))); } -// float64 func (f *Fmt) g64(a float64) *Fmt { - if f.InfOrNan(a) { - return f; - } - f1 := New(); - f2 := New(); - if f.wid_present { - f1.w(f.wid); - f2.w(f.wid); - } - if f.prec_present { - f1.p(f.prec); - f2.p(f.prec); - } - efmt := f1.e64(a).str(); - ffmt := f2.f64(a).str(); - // ffmt can return e in my bogus world; don't trim trailing 0s if so. - f_is_e := false; - for i := 0; i < len(ffmt); i++ { - if ffmt[i] == 'e' { - f_is_e = true; - break; - } - } - if !f_is_e { - // strip trailing zeros - l := len(ffmt); - for ffmt[l-1]=='0' { - l--; - } - ffmt = ffmt[0:l]; - } - if len(efmt) < len(ffmt) { - f.pad(efmt); - } else { - f.pad(ffmt); - } - f.clearflags(); - return f; + return FmtString(f, strconv.ftoa64(a, 'g', Prec(f, -1))); +} + +func (f *Fmt) fb64(a float64) *Fmt { + return FmtString(f, strconv.ftoa64(a, 'b', 0)); +} + +// float32 +// cannot defer to float64 versions +// because it will get rounding wrong in corner cases. +func (f *Fmt) e32(a float32) *Fmt { + return FmtString(f, strconv.ftoa32(a, 'e', Prec(f, -1))); +} + +func (f *Fmt) f32(a float32) *Fmt { + return FmtString(f, strconv.ftoa32(a, 'f', Prec(f, 6))); +} + +func (f *Fmt) g32(a float32) *Fmt { + return FmtString(f, strconv.ftoa32(a, 'g', Prec(f, -1))); +} + +func (f *Fmt) fb32(a float32) *Fmt { + return FmtString(f, strconv.ftoa32(a, 'b', 0)); } // float -func (x *Fmt) f32(a float32) *Fmt { - return x.f64(float64(a)) -} - func (x *Fmt) f(a float) *Fmt { + if strconv.floatsize == 32 { + return x.f32(float32(a)) + } return x.f64(float64(a)) } -// float -func (x *Fmt) e32(a float32) *Fmt { - return x.e64(float64(a)) -} - func (x *Fmt) e(a float) *Fmt { + if strconv.floatsize == 32 { + return x.e32(float32(a)) + } return x.e64(float64(a)) } -// float -func (x *Fmt) g32(a float32) *Fmt { +func (x *Fmt) g(a float) *Fmt { + if strconv.floatsize == 32 { + return x.g32(float32(a)) + } return x.g64(float64(a)) } -func (x *Fmt) g(a float) *Fmt { - return x.g64(float64(a)) +func (x *Fmt) fb(a float) *Fmt { + if strconv.floatsize == 32 { + return x.fb32(float32(a)) + } + return x.fb64(float64(a)) } diff --git a/src/lib/fmt/print.go b/src/lib/fmt/print.go index 8fa337f807..ce7a4f2d39 100644 --- a/src/lib/fmt/print.go +++ b/src/lib/fmt/print.go @@ -230,12 +230,24 @@ func getString(v reflect.Value) (val string, ok bool) { return "", false; } -func getFloat(v reflect.Value) (val float64, ok bool) { +func getFloat32(v reflect.Value) (val float32, ok bool) { + switch v.Kind() { + case reflect.Float32Kind: + return float32(v.(reflect.Float32Value).Get()), true; + case reflect.FloatKind: + if v.Type().Size()*8 == 32 { + return float32(v.(reflect.FloatValue).Get()), true; + } + } + return 0.0, false; +} + +func getFloat64(v reflect.Value) (val float64, ok bool) { switch v.Kind() { case reflect.FloatKind: - return float64(v.(reflect.FloatValue).Get()), true; - case reflect.Float32Kind: - return float64(v.(reflect.Float32Value).Get()), true; + if v.Type().Size()*8 == 64 { + return float64(v.(reflect.FloatValue).Get()), true; + } case reflect.Float64Kind: return float64(v.(reflect.Float64Value).Get()), true; case reflect.Float80Kind: @@ -299,9 +311,20 @@ func (p *P) printField(field reflect.Value) (was_string bool) { case reflect.UintKind, reflect.Uint8Kind, reflect.Uint16Kind, reflect.Uint32Kind, reflect.Uint64Kind: v, signed, ok := getInt(field); s = p.fmt.ud64(uint64(v)).str(); - case reflect.FloatKind, reflect.Float32Kind, reflect.Float64Kind, reflect.Float80Kind: - v, ok := getFloat(field); + case reflect.Float32Kind: + v, ok := getFloat32(field); + s = p.fmt.g32(v).str(); + case reflect.Float64Kind, reflect.Float80Kind: + v, ok := getFloat64(field); s = p.fmt.g64(v).str(); + case reflect.FloatKind: + if field.Type().Size()*8 == 32 { + v, ok := getFloat32(field); + s = p.fmt.g32(v).str(); + } else { + v, ok := getFloat64(field); + s = p.fmt.g64(v).str(); + } case reflect.StringKind: v, ok := getString(field); s = p.fmt.s(v).str(); @@ -400,6 +423,10 @@ func (p *P) doprintf(format string, v reflect.StructValue) { case 'b': if v, signed, ok := getInt(field); ok { s = p.fmt.b64(uint64(v)).str() // always unsigned + } else if v, ok := getFloat32(field); ok { + s = p.fmt.fb32(v).str() + } else if v, ok := getFloat64(field); ok { + s = p.fmt.fb64(v).str() } else { goto badtype } @@ -442,19 +469,25 @@ func (p *P) doprintf(format string, v reflect.StructValue) { // float case 'e': - if v, ok := getFloat(field); ok { + if v, ok := getFloat32(field); ok { + s = p.fmt.e32(v).str() + } else if v, ok := getFloat64(field); ok { s = p.fmt.e64(v).str() } else { goto badtype } case 'f': - if v, ok := getFloat(field); ok { + if v, ok := getFloat32(field); ok { + s = p.fmt.f32(v).str() + } else if v, ok := getFloat64(field); ok { s = p.fmt.f64(v).str() } else { goto badtype } case 'g': - if v, ok := getFloat(field); ok { + if v, ok := getFloat32(field); ok { + s = p.fmt.g32(v).str() + } else if v, ok := getFloat64(field); ok { s = p.fmt.g64(v).str() } else { goto badtype diff --git a/src/lib/http/server.go b/src/lib/http/server.go index 43b4fb1fd5..7aef824566 100644 --- a/src/lib/http/server.go +++ b/src/lib/http/server.go @@ -13,7 +13,7 @@ import ( "os"; "net"; "http"; - "strings" + "strconv"; ) // Serve a new connection. @@ -39,7 +39,7 @@ func ServeConnection(fd net.Conn, raddr string, f *(*Conn, *Request)) { export func Serve(l net.Listener, f *(*Conn, *Request)) *os.Error { // TODO: Make this unnecessary s, e := os.Getenv("GOMAXPROCS"); - if n, ok := strings.atoi(s); n < 3 { + if n, ok := strconv.atoi(s); n < 3 { print("Warning: $GOMAXPROCS needs to be at least 3.\n"); } diff --git a/src/lib/make.bash b/src/lib/make.bash index 820fdbf62f..b255b081da 100755 --- a/src/lib/make.bash +++ b/src/lib/make.bash @@ -31,10 +31,11 @@ rm -f *.6 buildfiles strings.go -builddirs syscall \ - math \ - os \ - reflect \ +builddirs syscall\ + math\ + os\ + strconv\ + reflect\ buildfiles io.go diff --git a/src/lib/net/net.go b/src/lib/net/net.go index 49d9c1bcc8..5db59091ea 100644 --- a/src/lib/net/net.go +++ b/src/lib/net/net.go @@ -7,8 +7,8 @@ package net import ( "os"; "net"; - "strings"; - "syscall" + "strconv"; + "syscall"; ) export var ( @@ -113,7 +113,7 @@ func SockaddrToHostPort(sa *syscall.Sockaddr) (hostport string, err *os.Error) { return "", e } host := IPToString(addr); - return JoinHostPort(host, strings.itoa(port)), nil; + return JoinHostPort(host, strconv.itoa(port)), nil; default: return "", UnknownSocketFamily } diff --git a/src/lib/reflect/test.go b/src/lib/reflect/test.go index 09b3b68410..8497380c78 100644 --- a/src/lib/reflect/test.go +++ b/src/lib/reflect/test.go @@ -72,9 +72,9 @@ func valuedump(s, t string) { case reflect.FloatKind: v.(reflect.FloatValue).Set(3200.0); case reflect.Float32Kind: - v.(reflect.Float32Value).Set(32.0); + v.(reflect.Float32Value).Set(32.1); case reflect.Float64Kind: - v.(reflect.Float64Value).Set(64.0); + v.(reflect.Float64Value).Set(64.2); case reflect.StringKind: v.(reflect.StringValue).Set("stringy cheese"); case reflect.BoolKind: @@ -136,8 +136,8 @@ func main() { valuedump("uint16", "16"); valuedump("uint32", "32"); valuedump("uint64", "64"); - valuedump("float32", "+3.200000e+01"); - valuedump("float64", "+6.400000e+01"); + valuedump("float32", "32.1"); + valuedump("float64", "64.2"); valuedump("string", "stringy cheese"); valuedump("bool", "true"); valuedump("*int8", "*int8(0)"); @@ -146,7 +146,7 @@ func main() { valuedump("**P.integer", "**P.integer(0)"); valuedump("*map[string]int32", "*map[string]int32(0)"); valuedump("*chan<-string", "*chan<-string(0)"); - valuedump("struct {c *chan *int32; d float32}", "struct{c *chan*int32; d float32}{*chan*int32(0), +0.000000e+00}"); + valuedump("struct {c *chan *int32; d float32}", "struct{c *chan*int32; d float32}{*chan*int32(0), 0}"); valuedump("*(a int8, b int32)", "*(a int8, b int32)(0)"); valuedump("struct {c *(? *chan *P.integer, ? *int8)}", "struct{c *(? *chan*P.integer, ? *int8)}{*(? *chan*P.integer, ? *int8)(0)}"); valuedump("struct {a int8; b int32}", "struct{a int8; b int32}{0, 0}"); @@ -158,7 +158,7 @@ func main() { } { var tmp = 123.4; value := reflect.NewValue(tmp); - assert(reflect.ValueToString(value), "+1.234000e+02"); + assert(reflect.ValueToString(value), "123.4"); } { var tmp = "abc"; value := reflect.NewValue(tmp); @@ -166,9 +166,9 @@ func main() { } { var i int = 7; - var tmp = &T{123, 456.0, "hello", &i}; + var tmp = &T{123, 456.75, "hello", &i}; value := reflect.NewValue(tmp); - assert(reflect.ValueToString(value.(reflect.PtrValue).Sub()), "main.T{123, +4.560000e+02, hello, *int(@)}"); + assert(reflect.ValueToString(value.(reflect.PtrValue).Sub()), "main.T{123, 456.75, hello, *int(@)}"); } { type C chan *T; // TODO: should not be necessary diff --git a/src/lib/reflect/tostring.go b/src/lib/reflect/tostring.go index 0a7004b311..f33f5272a2 100644 --- a/src/lib/reflect/tostring.go +++ b/src/lib/reflect/tostring.go @@ -9,7 +9,7 @@ package reflect import ( "reflect"; - "strings"; + "strconv"; ) export func TypeToString(typ Type, expand bool) string @@ -81,7 +81,7 @@ func TypeToString(typ Type, expand bool) string { if a.Open() { str = "[]" } else { - str = "[" + strings.ltoa(int64(a.Len())) + "]" + str = "[" + strconv.itoa64(int64(a.Len())) + "]" } return str + TypeToString(a.Elem(), false); case MapKind: @@ -120,11 +120,7 @@ func TypeToString(typ Type, expand bool) string { // TODO: want an unsigned one too func integer(v int64) string { - return strings.ltoa(v); -} - -func floatingpoint(v float64) string { - return strings.f64toa(v); + return strconv.itoa64(v); } func ValueToString(val Value) string { @@ -154,11 +150,15 @@ func ValueToString(val Value) string { case Uint64Kind: return integer(int64(val.(Uint64Value).Get())); case FloatKind: - return floatingpoint(float64(val.(FloatValue).Get())); + if strconv.floatsize == 32 { + return strconv.ftoa32(float32(val.(FloatValue).Get()), 'g', -1); + } else { + return strconv.ftoa64(float64(val.(FloatValue).Get()), 'g', -1); + } case Float32Kind: - return floatingpoint(float64(val.(Float32Value).Get())); + return strconv.ftoa32(val.(Float32Value).Get(), 'g', -1); case Float64Kind: - return floatingpoint(float64(val.(Float64Value).Get())); + return strconv.ftoa64(val.(Float64Value).Get(), 'g', -1); case Float80Kind: return "float80"; case StringKind: diff --git a/src/lib/strconv/Makefile b/src/lib/strconv/Makefile new file mode 100644 index 0000000000..e34a0fa92c --- /dev/null +++ b/src/lib/strconv/Makefile @@ -0,0 +1,58 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m strconv atof.go atoi.go decimal.go ftoa.go itoa.go +O=6 +GC=$(O)g +CC=$(O)c -w +AS=$(O)a +AR=$(O)ar + +PKG=$(GOROOT)/pkg/strconv.a + +install: $(PKG) + +nuke: clean + rm -f $(PKG) + +clean: + rm -f *.$O *.a + +%.$O: %.go + $(GC) $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + + +O1=\ + atoi.$O\ + decimal.$O\ + itoa.$O\ + +O2=\ + ftoa.$O\ + +O3=\ + atof.$O\ + +$(PKG): a1 a2 a3 +a1: $(O1) + $(AR) grc $(PKG) $(O1) + rm -f $(O1) +a2: $(O2) + $(AR) grc $(PKG) $(O2) + rm -f $(O2) +a3: $(O3) + $(AR) grc $(PKG) $(O3) + rm -f $(O3) + +$(O1): nuke +$(O2): a1 +$(O3): a2 + diff --git a/src/lib/strconv/atof.go b/src/lib/strconv/atof.go new file mode 100644 index 0000000000..2a34e8d079 --- /dev/null +++ b/src/lib/strconv/atof.go @@ -0,0 +1,220 @@ +// Copyright 2009 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. + +// Decimal to binary floating point conversion. +// Algorithm: +// 1) Store input in multiprecision decimal. +// 2) Multiply/divide decimal by powers of two until in range [0.5, 1) +// 3) Multiply by 2^precision and round to get mantissa. + +package strconv + +import "strconv" + +// TODO(rsc): Better truncation handling, check for overflow in exponent. +func StringToDecimal(s string) (neg bool, d *Decimal, trunc bool, ok bool) { + i := 0; + + // optional sign + if i >= len(s) { + return; + } + switch { + case s[i] == '+': + i++; + case s[i] == '-': + neg = true; + i++; + } + + // digits + b := new(Decimal); + sawdot := false; + sawdigits := false; + for ; i < len(s); i++ { + switch { + case s[i] == '.': + if sawdot { + return; + } + sawdot = true; + b.dp = b.nd; + continue; + + case '0' <= s[i] && s[i] <= '9': + sawdigits = true; + if s[i] == '0' && b.nd == 0 { // ignore leading zeros + b.dp--; + continue; + } + b.d[b.nd] = s[i]; + b.nd++; + continue; + } + break; + } + if !sawdigits { + return; + } + if !sawdot { + b.dp = b.nd; + } + + // optional exponent moves decimal point + if i < len(s) && 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++ { + e = e*10 + int(s[i]) - '0'; + } + b.dp += e*esign; + } + + if i != len(s) { + return; + } + + d = b; + ok = true; + return; +} + +// Decimal power of ten to binary power of two. +var powtab = []int{ + 1, 3, 6, 9, 13, 16, 19, 23, 26 +} + +func DecimalToFloatBits(neg bool, d *Decimal, trunc bool, flt *FloatInfo) (b uint64, overflow bool) { + // Zero is always a special case. + if d.nd == 0 { + return 0, false + } + + // TODO: check for obvious overflow + + // Scale by powers of two until in range [0.5, 1.0) + exp := 0; + for d.dp > 0 { + var n int; + if d.dp >= len(powtab) { + n = 27; + } else { + n = powtab[d.dp]; + } + d.Shift(-n); + exp += n; + } + for d.dp < 0 || d.dp == 0 && d.d[0] < '5' { + var n int; + if -d.dp >= len(powtab) { + n = 27; + } else { + n = powtab[-d.dp]; + } + d.Shift(n); + exp -= n; + } + + // Our range is [0.5,1) but floating point range is [1,2). + exp--; + + // Minimum representable exponent is flt.bias+1. + // If the exponent is smaller, move it up and + // adjust d accordingly. + if exp < flt.bias+1 { + n := flt.bias+1 - exp; + d.Shift(-n); + exp += n; + } + + // TODO: overflow/underflow + + // Extract 1+flt.mantbits bits. + mant := d.Shift(int(1+flt.mantbits)).RoundedInteger(); + + // Denormalized? + if mant&(1< '9' { + return 0, false + } + n = n*10 + uint64(s[i] - '0') + } + return n, true +} + +// Convert decimal string to integer. +// TODO: Doesn't check for overflow. +export func atoi64(s string) (i int64, ok bool) { + // empty string bad + if len(s) == 0 { + return 0, false + } + + // pick off leading sign + neg := false; + if s[0] == '+' { + s = s[1:len(s)] + } else if s[0] == '-' { + neg = true; + s = s[1:len(s)] + } + + var un uint64; + un, ok = atoui64(s); + if !ok { + return 0, false + } + n := int64(un); + if neg { + n = -n + } + return n, true +} + +export func atoui(s string) (i uint, ok bool) { + ii, okok := atoui64(s); + i = uint(ii); + return i, okok +} + +export func atoi(s string) (i int, ok bool) { + ii, okok := atoi64(s); + i = int(ii); + return i, okok +} diff --git a/src/lib/strconv/decimal.go b/src/lib/strconv/decimal.go new file mode 100644 index 0000000000..ee6dd0e780 --- /dev/null +++ b/src/lib/strconv/decimal.go @@ -0,0 +1,385 @@ +// Copyright 2009 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. + +// Multiprecision decimal numbers. +// For floating-point formatting only; not general purpose. +// Only operations are assign and (binary) left/right shift. +// Can do binary floating point in multiprecision decimal precisely +// because 2 divides 10; cannot do decimal floating point +// in multiprecision binary precisely. + +package strconv + +package type Decimal struct { + // TODO(rsc): Can make d[] a bit smaller and add + // truncated bool; + d [2000] byte; // digits + nd int; // number of digits used + dp int; // decimal point +}; +func (a *Decimal) String() string; +func (a *Decimal) Assign(v uint64); +func (a *Decimal) Shift(k int) *Decimal; +func (a *Decimal) Round(nd int) *Decimal; +func (a *Decimal) RoundUp(nd int) *Decimal; +func (a *Decimal) RoundDown(nd int) *Decimal; +func (a *Decimal) RoundedInteger() uint64; + + +func Copy(dst *[]byte, src *[]byte) int; +func DigitZero(dst *[]byte) int; + +func (a *Decimal) String() string { + n := 10 + a.nd; + if a.dp > 0 { + n += a.dp; + } + if a.dp < 0 { + n += -a.dp; + } + + buf := new([]byte, n); + w := 0; + switch { + case a.dp <= 0: + // zeros fill space between decimal point and digits + buf[w] = '0'; + w++; + buf[w] = '.'; + w++; + w += DigitZero(buf[w:w+-a.dp]); + w += Copy(buf[w:w+a.nd], (&a.d)[0:a.nd]); + + case a.dp < a.nd: + // decimal point in middle of digits + w += Copy(buf[w:w+a.dp], (&a.d)[0:a.dp]); + buf[w] = '.'; + w++; + w += Copy(buf[w:w+a.nd-a.dp], (&a.d)[a.dp:a.nd]); + + default: + // zeros fill space between digits and decimal point + w += Copy(buf[w:w+a.nd], (&a.d)[0:a.nd]); + w += DigitZero(buf[w:w+a.dp-a.nd]); + } + return string(buf[0:w]); +} + +func Copy(dst *[]byte, src *[]byte) int { + for i := 0; i < len(dst); i++ { + dst[i] = src[i]; + } + return len(dst); +} + +func DigitZero(dst *[]byte) int { + for i := 0; i < len(dst); i++ { + dst[i] = '0'; + } + return len(dst); +} + +// Trim trailing zeros from number. +// (They are meaningless; the decimal point is tracked +// independent of the number of digits.) +func Trim(a *Decimal) { + for a.nd > 0 && a.d[a.nd-1] == '0' { + a.nd--; + } + if a.nd == 0 { + a.dp = 0; + } +} + +// Assign v to a. +func (a *Decimal) Assign(v uint64) { + var buf [50]byte; + + // Write reversed decimal in buf. + n := 0; + for v > 0 { + v1 := v/10; + v -= 10*v1; + buf[n] = byte(v + '0'); + n++; + v = v1; + } + + // Reverse again to produce forward decimal in a.d. + a.nd = 0; + for n--; n>=0; n-- { + a.d[a.nd] = buf[n]; + a.nd++; + } + a.dp = a.nd; + Trim(a); +} + +package func NewDecimal(i uint64) *Decimal { + a := new(Decimal); + a.Assign(i); + return a; +} + +// Maximum shift that we can do in one pass without overflow. +// Signed int has 31 bits, and we have to be able to accomodate 9<>k == 0; r++ { + if r >= a.nd { + if n == 0 { + a.nd = 0; + return; + } + for n >> k == 0 { + n = n*10; + r++; + } + break; + } + c := int(a.d[r]); + n = n*10 + c-'0'; + } + a.dp -= r-1; + + // Pick up a digit, put down a digit. + for ; r < a.nd; r++ { + c := int(a.d[r]); + dig := n>>k; + n -= dig< 0 { + dig := n>>k; + n -= dig<= len(b) { + return true; + } + if b[i] != s[i] { + return b[i] < s[i]; + } + } + return false; +} + +// Binary shift left (/ 2) by k bits. k <= MaxShift to avoid overflow. +func LeftShift(a *Decimal, k uint) { + delta := leftcheat[k].delta; + if PrefixIsLessThan((&a.d)[0:a.nd], leftcheat[k].cutoff) { + delta--; + } + + r := a.nd; // read index + w := a.nd + delta; // write index + n := 0; + + // Pick up a digit, put down a digit. + for r--; r >= 0; r-- { + n += (int(a.d[r])-'0') << k; + quo := n/10; + rem := n - 10*quo; + w--; + a.d[w] = byte(rem+'0'); + n = quo; + } + + // Put down extra digits. + for n > 0 { + quo := n/10; + rem := n - 10*quo; + w--; + a.d[w] = byte(rem+'0'); + n = quo; + } + + if w != 0 { + // TODO: Remove - has no business panicking. + panic("fmt: bad LeftShift"); + } + a.nd += delta; + a.dp += delta; + Trim(a); +} + +// Binary shift left (k > 0) or right (k < 0). +// Returns receiver for convenience. +func (a *Decimal) Shift(k int) *Decimal { + switch { + case k > 0: + for k > MaxShift { + LeftShift(a, MaxShift); + k -= MaxShift; + } + LeftShift(a, uint(k)); + case k < 0: + for k < -MaxShift { + RightShift(a, MaxShift); + k += MaxShift; + } + RightShift(a, uint(-k)); + } + return a; +} + +// If we chop a at nd digits, should we round up? +func ShouldRoundUp(a *Decimal, nd int) bool { + if nd <= 0 || nd >= a.nd { + return false; + } + if a.d[nd] == '5' && nd+1 == a.nd { // exactly halfway - round to even + return (a.d[nd-1] - '0') % 2 != 0; + } + // not halfway - digit tells all + return a.d[nd] >= '5'; +} + +// Round a to nd digits (or fewer). +// Returns receiver for convenience. +func (a *Decimal) Round(nd int) *Decimal { + if nd <= 0 || nd >= a.nd { + return a; + } + if(ShouldRoundUp(a, nd)) { + return a.RoundUp(nd); + } + return a.RoundDown(nd); +} + +// Round a down to nd digits (or fewer). +// Returns receiver for convenience. +func (a *Decimal) RoundDown(nd int) *Decimal { + if nd <= 0 || nd >= a.nd { + return a; + } + a.nd = nd; + Trim(a); + return a; +} + +// Round a up to nd digits (or fewer). +// Returns receiver for convenience. +func (a *Decimal) RoundUp(nd int) *Decimal { + if nd <= 0 || nd >= a.nd { + return a; + } + + // round up + for i := nd-1; i >= 0; i-- { + c := a.d[i]; + if c < '9' { // can stop after this digit + a.d[i]++; + a.nd = i+1; + return a; + } + } + + // Number is all 9s. + // Change to single 1 with adjusted decimal point. + a.d[0] = '1'; + a.nd = 1; + a.dp++; + return a; +} + +// Extract integer part, rounded appropriately. +// No guarantees about overflow. +func (a *Decimal) RoundedInteger() uint64 { + if a.dp > 20 { + return 0xFFFFFFFFFFFFFFFF; + } + var i int; + n := uint64(0); + for i = 0; i < a.dp && i < a.nd; i++ { + n = n*10 + uint64(a.d[i] - '0'); + } + for ; i < a.dp; i++ { + n *= 10; + } + if ShouldRoundUp(a, a.dp) { + n++; + } + return n; +} + diff --git a/src/lib/strconv/ftoa.go b/src/lib/strconv/ftoa.go new file mode 100644 index 0000000000..f785c85645 --- /dev/null +++ b/src/lib/strconv/ftoa.go @@ -0,0 +1,379 @@ +// Copyright 2009 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. + +// Binary to decimal floating point conversion. +// Algorithm: +// 1) store mantissa in multiprecision decimal +// 2) shift decimal by exponent +// 3) read digits out & format + +package strconv + +import "strconv" + +// TODO: move elsewhere? +package type FloatInfo struct { + mantbits uint; + expbits uint; + bias int; +} +package var float32info = FloatInfo{ 23, 8, -127 } +package var float64info = FloatInfo{ 52, 11, -1023 } + +func FmtB(neg bool, mant uint64, exp int, flt *FloatInfo) string +func FmtE(neg bool, d *Decimal, prec int) string +func FmtF(neg bool, d *Decimal, prec int) string +func GenericFtoa(bits uint64, fmt byte, prec int, flt *FloatInfo) string +func Max(a, b int) int +func RoundShortest(d *Decimal, mant uint64, exp int, flt *FloatInfo) + +func FloatSize() int { + // Figure out whether float is float32 or float64. + // 1e-35 is representable in both, but 1e-70 + // is too small for a float32. + var f float = 1e-35; + if f*f == 0 { + return 32; + } + return 64; +} +export var floatsize = FloatSize() + +export func ftoa32(f float32, fmt byte, prec int) string { + return GenericFtoa(uint64(sys.float32bits(f)), fmt, prec, &float32info); +} + +export func ftoa64(f float64, fmt byte, prec int) string { + return GenericFtoa(sys.float64bits(f), fmt, prec, &float64info); +} + +export func ftoa(f float, fmt byte, prec int) string { + if floatsize == 32 { + return ftoa32(float32(f), fmt, prec); + } + return ftoa64(float64(f), fmt, prec); +} + +func GenericFtoa(bits uint64, fmt byte, prec int, flt *FloatInfo) string { + neg := bits>>flt.expbits>>flt.mantbits != 0; + exp := int(bits>>flt.mantbits) & (1< d.nd { + prec = d.nd; + } + // %e is used if the exponent from the conversion + // is less than -4 or greater than or equal to the precision. + exp := d.dp - 1; + if exp < -4 || exp >= prec { + return FmtE(neg, d, prec - 1); + } + return FmtF(neg, d, Max(prec - d.dp, 0)); + } + + return "%" + string(fmt); +} + +// Round d (= mant * 2^exp) to the shortest number of digits +// that will let the original floating point value be precisely +// reconstructed. Size is original floating point size (64 or 32). +func RoundShortest(d *Decimal, mant uint64, exp int, flt *FloatInfo) { + // TODO: Unless exp == minexp, if the number of digits in d + // is less than 17, it seems unlikely that it could not be + // the shortest possible number already. So maybe we can + // bail out without doing the extra multiprecision math here. + + // Compute upper and lower such that any decimal number + // between upper and lower (possibly inclusive) + // will round to the original floating point number. + + // 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. + upper := NewDecimal(mant*2+1).Shift(exp-int(flt.mantbits)-1); + + // d = mant << (exp - mantbits) + // Next lowest floating point number is mant-1 << exp-mantbits, + // 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. + minexp := flt.bias + 1; // minimum possible exponent + var mantlo uint64; + var explo int; + if mant > 1< 0 { + buf[w] = '.'; + w++; + for i := 0; i < prec; i++ { + if 1+i < d.nd { + buf[w] = d.d[1+i]; + } else { + buf[w] = '0'; + } + w++; + } + } + + // e± + buf[w] = 'e'; + w++; + exp := d.dp - 1; + if d.nd == 0 { // special case: 0 has exponent 0 + exp = 0; + } + if exp < 0 { + buf[w] = '-'; + exp = -exp; + } else { + buf[w] = '+'; + } + w++; + + // dddd + // count digits + n := 0; + for e := exp; e > 0; e /= 10 { + n++; + } + // leading zeros + for i := n; i < 2; i++ { + buf[w] = '0'; + w++; + } + // digits + w += n; + n = 0; + for e := exp; e > 0; e /= 10 { + n++; + buf[w-n] = byte(e%10 + '0'); + } + + return string(buf[0:w]); +} + +// %f: -ddddddd.ddddd +func FmtF(neg bool, d *Decimal, prec int) string { + buf := new([]byte, 1+Max(d.dp, 1)+1+Max(prec, 0)); + w := 0; + + // sign + if neg { + buf[w] = '-'; + w++; + } + + // integer, padded with zeros as needed. + if d.dp > 0 { + var i int; + for i = 0; i < d.dp && i < d.nd; i++ { + buf[w] = d.d[i]; + w++; + } + for ; i < d.dp; i++ { + buf[w] = '0'; + w++; + } + } else { + buf[w] = '0'; + w++; + } + + // fraction + if prec > 0 { + buf[w] = '.'; + w++; + for i := 0; i < prec; i++ { + if d.dp+i < 0 || d.dp+i >= d.nd { + buf[w] = '0'; + } else { + buf[w] = d.d[d.dp+i]; + } + w++; + } + } + + return string(buf[0:w]); +} + +// %b: -ddddddddp+ddd +func FmtB(neg bool, mant uint64, exp int, flt *FloatInfo) string { + var buf [50]byte; + w := len(buf); + exp -= int(flt.mantbits); + esign := byte('+'); + if exp < 0 { + esign = '-'; + exp = -exp; + } + n := 0; + for exp > 0 || n < 1 { + n++; + w--; + buf[w] = byte(exp%10 + '0'); + exp /= 10 + } + w--; + buf[w] = esign; + w--; + buf[w] = 'p'; + n = 0; + for mant > 0 || n < 1 { + n++; + w--; + buf[w] = byte(mant%10 + '0'); + mant /= 10; + } + if neg { + w--; + buf[w] = '-'; + } + return string((&buf)[w:len(buf)]); +} + +func Max(a, b int) int { + if a > b { + return a; + } + return b; +} + diff --git a/src/lib/strconv/itoa.go b/src/lib/strconv/itoa.go new file mode 100644 index 0000000000..8cac97161d --- /dev/null +++ b/src/lib/strconv/itoa.go @@ -0,0 +1,38 @@ +// Copyright 2009 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 + +export func itoa64(i int64) string { + if i == 0 { + return "0" + } + + neg := false; // negative + u := uint(i); + if i < 0 { + neg = true; + u = -u; + } + + // Assemble decimal in reverse order. + var b [32]byte; + bp := len(b); + for ; u > 0; u /= 10 { + bp--; + b[bp] = byte(u%10) + '0' + } + if neg { // add sign + bp--; + b[bp] = '-' + } + + // BUG return string(b[bp:len(b)]) + return string((&b)[bp:len(b)]) +} + +export func itoa(i int) string { + return itoa64(int64(i)); +} + diff --git a/src/lib/strconv/test.bash b/src/lib/strconv/test.bash new file mode 100755 index 0000000000..5da7772498 --- /dev/null +++ b/src/lib/strconv/test.bash @@ -0,0 +1,20 @@ +#!/bin/bash +# Copyright 2009 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. + +set -e +set -x + +make clean +make +6g testatof.go +6l testatof.6 +6.out +6g testftoa.go +6l testftoa.6 +6.out +6g testfp.go +6l testfp.6 +6.out +rm -f *.6 6.out diff --git a/src/lib/strconv/testatof.go b/src/lib/strconv/testatof.go new file mode 100644 index 0000000000..df3396b8d5 --- /dev/null +++ b/src/lib/strconv/testatof.go @@ -0,0 +1,46 @@ +// Copyright 2009 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 main + +import "strconv" + +type Test struct { + in string; + out string; +} + +var tests = []Test { + Test{ "1", "1" }, + Test{ "1e23", "1e+23" }, + Test{ "100000000000000000000000", "1e+23" }, + Test{ "1e-100", "1e-100" }, + Test{ "123456700", "1.234567e+08" }, + Test{ "99999999999999974834176", "9.999999999999997e+22" }, + Test{ "100000000000000000000001", "1.0000000000000001e+23" }, + Test{ "100000000000000008388608", "1.0000000000000001e+23" }, + Test{ "100000000000000016777215", "1.0000000000000001e+23" }, + Test{ "100000000000000016777216", "1.0000000000000003e+23" }, + Test{ "-1", "-1" }, + Test{ "-0", "0" }, +} + +func main() { + bad := 0; + for i := 0; i < len(tests); i++ { + t := &tests[i]; + f, overflow, ok := strconv.atof64(t.in); + if !ok { + panicln("test", t.in); + } + s := strconv.ftoa64(f, 'g', -1); + if s != t.out { + println("test", t.in, "want", t.out, "got", s); + bad++; + } + } + if bad != 0 { + panic("failed"); + } +} diff --git a/src/lib/strconv/testfp.go b/src/lib/strconv/testfp.go new file mode 100644 index 0000000000..65428b9777 --- /dev/null +++ b/src/lib/strconv/testfp.go @@ -0,0 +1,156 @@ +// Copyright 2009 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 main + +import ( + "bufio"; + "fmt"; + "os"; + "strconv"; + "strings"; +) + +func pow2(i int) float64 { + switch { + case i < 0: + return 1 / pow2(-i); + case i == 0: + return 1; + case i == 1: + return 2; + } + return pow2(i/2) * pow2(i-i/2); +} + +// Wrapper around strconv.atof64. Handles dddddp+ddd (binary exponent) +// itself, passes the rest on to strconv.atof64. +func atof64(s string) (f float64, ok bool) { + a := strings.split(s, "p"); + if len(a) == 2 { + n, ok := strconv.atoi64(a[0]); + if !ok { + return 0, false; + } + e, ok1 := strconv.atoi(a[1]); + if !ok1 { + println("bad e", a[1]); + return 0, false; + } + v := float64(n); + // We expect that v*pow2(e) fits in a float64, + // but pow2(e) by itself may not. Be careful. + if e <= -1000 { + v *= pow2(-1000); + e += 1000; + for e < 0 { + v /= 2; + e++; + } + return v, true; + } + if e >= 1000 { + v *= pow2(1000); + e -= 1000; + for e > 0 { + v *= 2; + e--; + } + return v, true; + } + return v*pow2(e), true; + } + f1, overflow, ok1 := strconv.atof64(s); + if !ok1 { + return 0, false; + } + return f1, true; +} + +// Wrapper around strconv.atof32. Handles dddddp+ddd (binary exponent) +// itself, passes the rest on to strconv.atof32. +func atof32(s string) (f float32, ok bool) { + a := strings.split(s, "p"); + if len(a) == 2 { + n, ok := strconv.atoi(a[0]); + if !ok { + println("bad n", a[0]); + return 0, false; + } + e, ok1 := strconv.atoi(a[1]); + if !ok1 { + println("bad p", a[1]); + return 0, false; + } + return float32(float64(n)*pow2(e)), true; + } + f1, overflow, ok1 := strconv.atof32(s); + if !ok1 { + return 0, false; + } + return f1, true; +} + +func main() +{ + fd, err := os.Open("testfp.txt", os.O_RDONLY, 0); + if err != nil { + panicln("testfp: open testfp.txt:", err.String()); + } + + b, err1 := bufio.NewBufRead(fd); + if err1 != nil { + panicln("testfp NewBufRead:", err1.String()); + } + + lineno := 0; + ok := true; + for { + line, err2 := b.ReadLineString('\n', false); + if err2 == bufio.EndOfFile { + break; + } + if err2 != nil { + panicln("testfp: read testfp.txt:", err2.String()); + } + lineno++; + if len(line) == 0 || line[0] == '#' { + continue + } + a := strings.split(line, " "); + if len(a) != 4 { + print("testfp.txt:", lineno, ": wrong field count\n"); + continue; + } + var s string; + var v float64; + switch a[0] { + case "float64": + var ok bool; + v, ok = atof64(a[2]); + if !ok { + print("testfp.txt:", lineno, ": cannot atof64 ", a[2]); + continue; + } + s = fmt.sprintf(a[1], v); + case "float32": + v1, ok := atof32(a[2]); + if !ok { + print("testfp.txt:", lineno, ": cannot atof32 ", a[2]); + continue; + } + s = fmt.sprintf(a[1], v1); + v = float64(v1); + } + if s != a[3] { + print("testfp.txt:", lineno, ": ", a[0], " ", a[1], " ", a[2], " (", v, ") ", + "want ", a[3], " got ", s, "\n"); + ok = false; + } +//else print("testfp.txt:", lineno, ": worked! ", s, "\n"); + } + if !ok { + panicln("testfp failed"); + } +} diff --git a/src/lib/strconv/testfp.txt b/src/lib/strconv/testfp.txt new file mode 100644 index 0000000000..08d3c4ef09 --- /dev/null +++ b/src/lib/strconv/testfp.txt @@ -0,0 +1,181 @@ +# Floating-point conversion test cases. +# Empty lines and lines beginning with # are ignored. +# The rest have four fields per line: type, format, input, and output. +# The input is given either in decimal or binary scientific notation. +# The output is the string that should be produced by formatting the +# input with the given format. +# +# The formats are as in C's printf, except that %b means print +# binary scientific notation: NpE = N x 2^E. + +# TODO: +# Powers of 10. +# Powers of 2. +# %.20g versions. +# random sources +# random targets +# random targets ± half a ULP + +# Difficult boundary cases, derived from tables given in +# Vern Paxson, A Program for Testing IEEE Decimal-Binary Conversion +# ftp://ftp.ee.lbl.gov/testbase-report.ps.Z + +# Table 1: Stress Inputs for Conversion to 53-bit Binary, < 1/2 ULP +float64 %b 5e+125 6653062250012735p+365 +float64 %b 69e+267 4705683757438170p+841 +float64 %b 999e-026 6798841691080350p-129 +float64 %b 7861e-034 8975675289889240p-153 +float64 %b 75569e-254 6091718967192243p-880 +float64 %b 928609e-261 7849264900213743p-900 +float64 %b 9210917e+080 8341110837370930p+236 +float64 %b 84863171e+114 4625202867375927p+353 +float64 %b 653777767e+273 5068902999763073p+884 +float64 %b 5232604057e-298 5741343011915040p-1010 +float64 %b 27235667517e-109 6707124626673586p-380 +float64 %b 653532977297e-123 7078246407265384p-422 +float64 %b 3142213164987e-294 8219991337640559p-988 +float64 %b 46202199371337e-072 5224462102115359p-246 +float64 %b 231010996856685e-073 5224462102115359p-247 +float64 %b 9324754620109615e+212 5539753864394442p+705 +float64 %b 78459735791271921e+049 8388176519442766p+166 +float64 %b 272104041512242479e+200 5554409530847367p+670 +float64 %b 6802601037806061975e+198 5554409530847367p+668 +float64 %b 20505426358836677347e-221 4524032052079546p-722 +float64 %b 836168422905420598437e-234 5070963299887562p-760 +float64 %b 4891559871276714924261e+222 6452687840519111p+757 + +# Table 2: Stress Inputs for Conversion to 53-bit Binary, > 1/2 ULP +float64 %b 9e-265 8168427841980010p-930 +float64 %b 85e-037 6360455125664090p-169 +float64 %b 623e+100 6263531988747231p+289 +float64 %b 3571e+263 6234526311072170p+833 +float64 %b 81661e+153 6696636728760206p+472 +float64 %b 920657e-023 5975405561110124p-109 +float64 %b 4603285e-024 5975405561110124p-110 +float64 %b 87575437e-309 8452160731874668p-1053 +float64 %b 245540327e+122 4985336549131723p+381 +float64 %b 6138508175e+120 4985336549131723p+379 +float64 %b 83356057653e+193 5986732817132056p+625 +float64 %b 619534293513e+124 4798406992060657p+399 +float64 %b 2335141086879e+218 5419088166961646p+713 +float64 %b 36167929443327e-159 8135819834632444p-536 +float64 %b 609610927149051e-255 4576664294594737p-850 +float64 %b 3743626360493413e-165 6898586531774201p-549 +float64 %b 94080055902682397e-242 6273271706052298p-800 +float64 %b 899810892172646163e+283 7563892574477827p+947 +float64 %b 7120190517612959703e+120 5385467232557565p+409 +float64 %b 25188282901709339043e-252 5635662608542340p-825 +float64 %b 308984926168550152811e-052 5644774693823803p-157 +float64 %b 6372891218502368041059e+064 4616868614322430p+233 + +# Table 3: Stress Inputs for Converting 53-bit Binary to Decimal, < 1/2 ULP +float64 %.0e 8511030020275656p-342 9e-88 +float64 %.1e 5201988407066741p-824 4.6e-233 +float64 %.2e 6406892948269899p+237 1.41e+87 +float64 %.3e 8431154198732492p+72 3.981e+37 +float64 %.4e 6475049196144587p+99 4.1040e+45 +float64 %.5e 8274307542972842p+726 2.92084e+234 +float64 %.6e 5381065484265332p-456 2.891946e-122 +float64 %.7e 6761728585499734p-1057 4.3787718e-303 +float64 %.8e 7976538478610756p+376 1.22770163e+129 +float64 %.9e 5982403858958067p+377 1.841552452e+129 +float64 %.10e 5536995190630837p+93 5.4835744350e+43 +float64 %.11e 7225450889282194p+710 3.89190181146e+229 +float64 %.12e 7225450889282194p+709 1.945950905732e+229 +float64 %.13e 8703372741147379p+117 1.4460958381605e+51 +float64 %.14e 8944262675275217p-1001 4.17367747458531e-286 +float64 %.15e 7459803696087692p-707 1.107950772878888e-197 +float64 %.16e 6080469016670379p-381 1.2345501366327440e-99 +float64 %.17e 8385515147034757p+721 9.25031711960365024e+232 +float64 %.18e 7514216811389786p-828 4.198047150284889840e-234 +float64 %.19e 8397297803260511p-345 1.1716315319786511046e-88 +float64 %.20e 6733459239310543p+202 4.32810072844612493629e+76 +float64 %.21e 8091450587292794p-473 3.317710118160031081518e-127 + +# Table 4: Stress Inputs for Converting 53-bit Binary to Decimal, > 1/2 ULP +float64 %.0e 6567258882077402p+952 3e+302 +float64 %.1e 6712731423444934p+535 7.6e+176 +float64 %.2e 6712731423444934p+534 3.78e+176 +float64 %.3e 5298405411573037p-957 4.350e-273 +float64 %.4e 5137311167659507p-144 2.3037e-28 +float64 %.5e 6722280709661868p+363 1.26301e+125 +float64 %.6e 5344436398034927p-169 7.142211e-36 +float64 %.7e 8369123604277281p-853 1.3934574e-241 +float64 %.8e 8995822108487663p-780 1.41463449e-219 +float64 %.9e 8942832835564782p-383 4.539277920e-100 +float64 %.10e 8942832835564782p-384 2.2696389598e-100 +float64 %.11e 8942832835564782p-385 1.13481947988e-100 +float64 %.12e 6965949469487146p-249 7.700366561890e-60 +float64 %.13e 6965949469487146p-250 3.8501832809448e-60 +float64 %.14e 6965949469487146p-251 1.92509164047238e-60 +float64 %.15e 7487252720986826p+548 6.898586531774201e+180 +float64 %.16e 5592117679628511p+164 1.3076622631878654e+65 +float64 %.17e 8887055249355788p+665 1.36052020756121240e+216 +float64 %.18e 6994187472632449p+690 3.592810217475959676e+223 +float64 %.19e 8797576579012143p+588 8.9125197712484551899e+192 +float64 %.20e 7363326733505337p+272 5.58769757362301140950e+97 +float64 %.21e 8549497411294502p-448 1.176257830728540379990e-119 + +# Table 14: Stress Inputs for Conversion to 24-bit Binary, <1/2 ULP +# NOTE: The lines with exponent p-149 have been changed from the +# paper. Those entries originally read p-150 and had a mantissa +# twice as large (and even), but IEEE single-precision has no p-150: +# that's the start of the denormals. +float32 %b 5e-20 15474250p-88 +float32 %b 67e+14 12479722p+29 +float32 %b 985e+15 14333636p+36 +# float32 %b 7693e-42 10979816p-150 +float32 %b 7693e-42 5489908p-149 +float32 %b 55895e-16 12888509p-61 +# float32 %b 996622e-44 14224264p-150 +float32 %b 996622e-44 7112132p-149 +float32 %b 7038531e-32 11420669p-107 +# float32 %b 60419369e-46 8623340p-150 +float32 %b 60419369e-46 4311670p-149 +float32 %b 702990899e-20 16209866p-61 +# float32 %b 6930161142e-48 9891056p-150 +float32 %b 6930161142e-48 4945528p-149 +float32 %b 25933168707e+13 14395800p+54 +float32 %b 596428896559e+20 12333860p+82 + +# Table 15: Stress Inputs for Conversion to 24-bit Binary, >1/2 ULP +float32 %b 3e-23 9507380p-98 +float32 %b 57e+18 12960300p+42 +float32 %b 789e-35 10739312p-130 +float32 %b 2539e-18 11990089p-72 +float32 %b 76173e+28 9845130p+86 +float32 %b 887745e-11 9760860p-40 +float32 %b 5382571e-37 11447463p-124 +float32 %b 82381273e-35 8554961p-113 +float32 %b 750486563e-38 9975678p-120 +float32 %b 3752432815e-39 9975678p-121 +float32 %b 75224575729e-45 13105970p-137 +float32 %b 459926601011e+15 12466336p+65 + +# Table 16: Stress Inputs for Converting 24-bit Binary to Decimal, < 1/2 ULP +float32 %.0e 12676506p-102 2e-24 +float32 %.1e 12676506p-103 1.2e-24 +float32 %.2e 15445013p+86 1.19e+33 +float32 %.3e 13734123p-138 3.941e-35 +float32 %.4e 12428269p-130 9.1308e-33 +float32 %.5e 15334037p-146 1.71900e-37 +float32 %.6e 11518287p-41 5.237910e-06 +float32 %.7e 12584953p-145 2.8216440e-37 +float32 %.8e 15961084p-125 3.75243281e-31 +float32 %.9e 14915817p-146 1.672120916e-37 +float32 %.10e 10845484p-102 2.1388945814e-24 +float32 %.11e 16431059p-61 7.12583594561e-12 + +# Table 17: Stress Inputs for Converting 24-bit Binary to Decimal, > 1/2 ULP +float32 %.0e 16093626p+69 1e+28 +float32 %.1e 9983778p+25 3.4e+14 +float32 %.2e 12745034p+104 2.59e+38 +float32 %.3e 12706553p+72 6.001e+28 +float32 %.4e 11005028p+45 3.8721e+20 +float32 %.5e 15059547p+71 3.55584e+28 +float32 %.6e 16015691p-99 2.526831e-23 +float32 %.7e 8667859p+56 6.2458507e+23 +float32 %.8e 14855922p-82 3.07213267e-18 +float32 %.9e 14855922p-83 1.536066333e-18 +float32 %.10e 10144164p-110 7.8147796834e-27 +float32 %.11e 13248074p+95 5.24810279937e+35 diff --git a/src/lib/strconv/testftoa.go b/src/lib/strconv/testftoa.go new file mode 100644 index 0000000000..2d72bf42e2 --- /dev/null +++ b/src/lib/strconv/testftoa.go @@ -0,0 +1,96 @@ +// Copyright 2009 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 main + +import "strconv" + +type Test struct { + f float64; + fmt byte; + prec int; + s string; +} + +var tests = []Test { + Test{ 1, 'e', 5, "1.00000e+00" }, + Test{ 1, 'f', 5, "1.00000" }, + Test{ 1, 'g', 5, "1" }, + Test{ 1, 'g', -1, "1" }, + + Test{ 0, 'e', 5, "0.00000e+00" }, + Test{ 0, 'f', 5, "0.00000" }, + Test{ 0, 'g', 5, "0" }, + Test{ 0, 'g', -1, "0" }, + + Test{ -1, 'e', 5, "-1.00000e+00" }, + Test{ -1, 'f', 5, "-1.00000" }, + Test{ -1, 'g', 5, "-1" }, + Test{ -1, 'g', -1, "-1" }, + + Test{ 12, 'e', 5, "1.20000e+01" }, + Test{ 12, 'f', 5, "12.00000" }, + Test{ 12, 'g', 5, "12" }, + Test{ 12, 'g', -1, "12" }, + + Test{ 123456700, 'e', 5, "1.23457e+08" }, + Test{ 123456700, 'f', 5, "123456700.00000" }, + Test{ 123456700, 'g', 5, "1.2346e+08" }, + Test{ 123456700, 'g', -1, "1.234567e+08" }, + + Test{ 1.2345e6, 'e', 5, "1.23450e+06" }, + Test{ 1.2345e6, 'f', 5, "1234500.00000" }, + Test{ 1.2345e6, 'g', 5, "1.2345e+06" }, + + Test{ 1e23, 'e', 17, "9.99999999999999916e+22" }, + Test{ 1e23, 'f', 17, "99999999999999991611392.00000000000000000" }, + Test{ 1e23, 'g', 17, "9.9999999999999992e+22" }, + + Test{ 1e23, 'e', -1, "1e+23" }, + Test{ 1e23, 'f', -1, "100000000000000000000000" }, + Test{ 1e23, 'g', -1, "1e+23" }, + + Test{ 1e23-8.5e6, 'e', 17, "9.99999999999999748e+22" }, + Test{ 1e23-8.5e6, 'f', 17, "99999999999999974834176.00000000000000000" }, + Test{ 1e23-8.5e6, 'g', 17, "9.9999999999999975e+22" }, + + Test{ 1e23-8.5e6, 'e', -1, "9.999999999999997e+22" }, + Test{ 1e23-8.5e6, 'f', -1, "99999999999999970000000" }, + Test{ 1e23-8.5e6, 'g', -1, "9.999999999999997e+22" }, + + Test{ 1e23+8.5e6, 'e', 17, "1.00000000000000008e+23" }, + Test{ 1e23+8.5e6, 'f', 17, "100000000000000008388608.00000000000000000" }, + Test{ 1e23+8.5e6, 'g', 17, "1.0000000000000001e+23" }, + + Test{ 1e23+8.5e6, 'e', -1, "1.0000000000000001e+23" }, + Test{ 1e23+8.5e6, 'f', -1, "100000000000000010000000" }, + Test{ 1e23+8.5e6, 'g', -1, "1.0000000000000001e+23" }, + + Test{ 32, 'g', -1, "32" }, +} + +func main() { + bad := 0; + if strconv.floatsize != 32 { + panic("floatsize: ", strconv.floatsize); + } + for i := 0; i < len(tests); i++ { + t := &tests[i]; + s := strconv.ftoa64(t.f, t.fmt, t.prec); + if s != t.s { + println("test", t.f, string(t.fmt), t.prec, "want", t.s, "got", s); + bad++; + } + if float64(float32(t.f)) == t.f { + s := strconv.ftoa32(float32(t.f), t.fmt, t.prec); + if s != t.s { + println("test32", t.f, string(t.fmt), t.prec, "want", t.s, "got", s); + bad++; + } + } + } + if bad != 0 { + panic("failed"); + } +} diff --git a/src/lib/strings.go b/src/lib/strings.go index 6953922974..433e500640 100644 --- a/src/lib/strings.go +++ b/src/lib/strings.go @@ -115,272 +115,3 @@ export func join(a *[]string, sep string) string { } return string(b) } - -// Convert decimal string to unsigned integer. -// TODO: Doesn't check for overflow. -export func atoui64(s string) (i uint64, ok bool) { - // empty string bad - if len(s) == 0 { - return 0, false - } - - // pick off zero - if s == "0" { - return 0, true - } - - // otherwise, leading zero bad - if s[0] == '0' { - return 0, false - } - - // parse number - n := uint64(0); - for i := 0; i < len(s); i++ { - if s[i] < '0' || s[i] > '9' { - return 0, false - } - n = n*10 + uint64(s[i] - '0') - } - return n, true -} - -// Convert decimal string to integer. -// TODO: Doesn't check for overflow. -export func atoi64(s string) (i int64, ok bool) { - // empty string bad - if len(s) == 0 { - return 0, false - } - - // pick off leading sign - neg := false; - if s[0] == '+' { - s = s[1:len(s)] - } else if s[0] == '-' { - neg = true; - s = s[1:len(s)] - } - - var un uint64; - un, ok = atoui64(s); - if !ok { - return 0, false - } - n := int64(un); - if neg { - n = -n - } - return n, true -} - -export func atoui(s string) (i uint, ok bool) { - ii, okok := atoui64(s); - i = uint(ii); - return i, okok -} - -export func atoi(s string) (i int, ok bool) { - ii, okok := atoi64(s); - i = int(ii); - return i, okok -} - -export func ltoa(i int64) string { - if i == 0 { - return "0" - } - - neg := false; // negative - u := uint(i); - if i < 0 { - neg = true; - u = -u; - } - - // Assemble decimal in reverse order. - var b [32]byte; - bp := len(b); - for ; u > 0; u /= 10 { - bp--; - b[bp] = byte(u%10) + '0' - } - if neg { // add sign - bp--; - b[bp] = '-' - } - - // BUG return string(b[bp:len(b)]) - return string((&b)[bp:len(b)]) -} - -export func itoa(i int) string { - return ltoa(int64(i)); -} - -// Convert float64 to string. No control over format. -// Result not great; only useful for simple debugging. -export func f64toa(v float64) string { - var buf [20]byte; - - const n = 7; // digits printed - e := 0; // exp - var sign byte = '+'; - if(v != 0) { - // sign - if(v < 0) { - v = -v; - sign = '-'; - } - - // normalize - for v >= 10 { - e++; - v /= 10; - } - for v < 1 { - e--; - v *= 10; - } - - // round - var h float64 = 5; - for i := 0; i < n; i++ { - h /= 10; - } - v += h; - if v >= 10 { - e++; - v /= 10; - } - } - - // format +d.dddd+edd - buf[0] = sign; - for i := 0; i < n; i++ { - s := int64(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] = '-'; - } - - // TODO: exponents > 99? - buf[n+4] = byte((e/10) + '0'); - buf[n+5] = byte((e%10) + '0'); - return string(buf)[0:n+6]; // TODO: should be able to slice buf -} - -export func ftoa(v float) string { - return f64toa(float64(v)); -} - -export func f32toa(v float32) string { - return f64toa(float64(v)); -} - -// Simple conversion of string to floating point. -// TODO: make much better. THIS CODE IS VERY WEAK. -// Lets through some poor cases such as "." and "e4" and "1e-". Fine. -export func atof64(s string) (f float64, ok bool) { - // empty string bad - if len(s) == 0 { - return 0, false - } - - // pick off leading sign - neg := false; - if s[0] == '+' { - s = s[1:len(s)] - } else if s[0] == '-' { - neg = true; - s = s[1:len(s)] - } - - // parse number - // first, left of the decimal point. - n := uint64(0); - i := 0; - for ; i < len(s); i++ { - if s[i] == '.' || s[i] == 'e' || s[i] == 'E' { - break - } - if s[i] < '0' || s[i] > '9' { - return 0, false - } - n = n*10 + uint64(s[i] - '0') - } - result := float64(n); - if i != len(s) { - frac := uint64(0); - scale := float64(1); - // decimal and fraction - if s[i] == '.' { - i++; - for ; i < len(s); i++ { - if s[i] == 'e' || s[i] == 'E' { - break - } - if s[i] < '0' || s[i] > '9' { - return 0, false - } - frac = frac*10 + uint64(s[i] - '0'); - scale = scale * 10.0; - } - } - result += float64(frac)/scale; - // exponent - if i != len(s) { // must be 'e' or 'E' - i++; - eneg := false; - if i < len(s) && s[i] == '-' { - eneg = true; - i++; - } else if i < len(s) && s[i] == '+' { - i++; - } - // this works ok for "1e+" - fine. - exp := uint64(0); - for ; i < len(s); i++ { - if s[i] < '0' || s[i] > '9' { - return 0, false - } - exp = exp*10 + uint64(s[i] - '0'); - } - if eneg { - for exp > 0 { - result /= 10.0; - exp--; - } - } else { - for exp > 0 { - result *= 10.0; - exp--; - } - } - } - } - - if neg { - result = -result - } - return result, true -} - -export func atof(s string) (f float, ok bool) { - a, b := atof64(s); - return float(a), b; -} - -export func atof32(s string) (f float32, ok bool) { - a, b := atof64(s); - return float32(a), b; -} diff --git a/src/run.bash b/src/run.bash index d412a6903d..c554636db4 100755 --- a/src/run.bash +++ b/src/run.bash @@ -11,6 +11,12 @@ xcd() { builtin cd $1 } +(xcd lib/strconv +make clean +time make +bash test.bash +) || exit $? + (xcd lib/reflect make clean time make diff --git a/test/chan/goroutines.go b/test/chan/goroutines.go index b86ed8848d..b480e5085f 100644 --- a/test/chan/goroutines.go +++ b/test/chan/goroutines.go @@ -10,7 +10,7 @@ package main import ( - "strings"; + "strconv"; ) func f(left, right *chan int) { @@ -21,7 +21,7 @@ func main() { var n = 10000; if sys.argc() > 1 { var ok bool; - n, ok = strings.atoi(sys.argv(1)); + n, ok = strconv.atoi(sys.argv(1)); if !ok { print("bad arg\n"); sys.exit(1); diff --git a/test/fmt_test.go b/test/fmt_test.go index f694a55af9..dfc7fdd886 100644 --- a/test/fmt_test.go +++ b/test/fmt_test.go @@ -44,8 +44,8 @@ func main() { E(f.s("\tb ").b(7), "\tb 111"); E(f.s("\tb64 ").b64(B64), "\tb64 1111111111111111111111111111111111111111111111111111111111111111"); E(f.s("\te ").e64(1.), "\te 1.000000e+00"); - E(f.s("\te ").e64(1234.5678e3), "\te 1.234567e+06"); - E(f.s("\te ").e64(1234.5678e-8), "\te 1.234567e-05"); + E(f.s("\te ").e64(1234.5678e3), "\te 1.234568e+06"); + E(f.s("\te ").e64(1234.5678e-8), "\te 1.234568e-05"); E(f.s("\te ").e64(-7.0), "\te -7.000000e+00"); E(f.s("\te ").e64(-1e-9), "\te -1.000000e-09"); E(f.s("\tf ").f64(1234.5678e3), "\tf 1234567.800000"); @@ -53,9 +53,9 @@ func main() { E(f.s("\tf ").f64(-7.0), "\tf -7.000000"); E(f.s("\tf ").f64(-1e-9), "\tf -0.000000"); E(f.s("\tg ").g64(1234.5678e3), "\tg 1234567.8"); - E(f.s("\tg ").g64(1234.5678e-8), "\tg 0.000012"); - E(f.s("\tg ").g64(-7.0), "\tg -7."); - E(f.s("\tg ").g64(-1e-9), "\tg -0."); + E(f.s("\tg ").g64(1234.5678e-8), "\tg 1.2345678e-05"); + E(f.s("\tg ").g64(-7.0), "\tg -7"); + E(f.s("\tg ").g64(-1e-9), "\tg -1e-09"); E(f.s("\tc ").c('x'), "\tc x"); E(f.s("\tc ").c(0xe4), "\tc ä"); E(f.s("\tc ").c(0x672c), "\tc 本"); @@ -74,9 +74,9 @@ func main() { E(f.s("\t-20.5s\t|").wp(-20,5).s("qwertyuiop").s("|"), "\t-20.5s\t|qwert |"); E(f.s("\t20c\t|").w(20).c('x').s("|"), "\t20c\t| x|"); E(f.s("\t-20c\t|").w(-20).c('x').s("|"), "\t-20c\t|x |"); - E(f.s("\t20e\t|").w(20).e(1.2345e3).s("|"), "\t20e\t| 1.234500e+03|"); - E(f.s("\t20e\t|").w(20).e(1.2345e-3).s("|"), "\t20e\t| 1.234500e-03|"); - E(f.s("\t-20e\t|").w(-20).e(1.2345e3).s("|"), "\t-20e\t|1.234500e+03 |"); + E(f.s("\t20e\t|").wp(20, 6).e(1.2345e3).s("|"), "\t20e\t| 1.234500e+03|"); + E(f.s("\t20e\t|").wp(20, 6).e(1.2345e-3).s("|"), "\t20e\t| 1.234500e-03|"); + E(f.s("\t-20e\t|").wp(-20, 6).e(1.2345e3).s("|"), "\t-20e\t|1.234500e+03 |"); E(f.s("\t20.8e\t|").wp(20,8).e(1.2345e3).s("|"), "\t20.8e\t| 1.23450000e+03|"); E(f.s("\t20f\t|").w(20).f64(1.23456789e3).s("|"), "\t20f\t| 1234.567890|"); E(f.s("\t20f\t|").w(20).f64(1.23456789e-3).s("|"), "\t20f\t| 0.001235|"); @@ -85,10 +85,10 @@ func main() { E(f.s("\t20.8f\t|").wp(20,8).f64(1.23456789e3).s("|"), "\t20.8f\t| 1234.56789000|"); E(f.s("\t20.8f\t|").wp(20,8).f64(1.23456789e-3).s("|"), "\t20.8f\t| 0.00123457|"); E(f.s("\tg\t|").g64(1.23456789e3).s("|"), "\tg\t|1234.56789|"); - E(f.s("\tg\t|").g64(1.23456789e-3).s("|"), "\tg\t|0.001235|"); - E(f.s("\tg\t|").g64(1.23456789e20).s("|"), "\tg\t|1.234567e+20|"); + E(f.s("\tg\t|").g64(1.23456789e-3).s("|"), "\tg\t|0.00123456789|"); + E(f.s("\tg\t|").g64(1.23456789e20).s("|"), "\tg\t|1.23456789e+20|"); - E(f.s("\tE\t|").w(20).g64(sys.Inf(1)).s("|"), "\tE\t| Inf|"); + E(f.s("\tE\t|").w(20).g64(sys.Inf(1)).s("|"), "\tE\t| +Inf|"); E(f.s("\tF\t|").w(-20).g64(sys.Inf(-1)).s("|"), "\tF\t|-Inf |"); E(f.s("\tG\t|").w(20).g64(sys.NaN()).s("|"), "\tG\t| NaN|"); } diff --git a/test/stringslib.go b/test/stringslib.go index e9a919cad6..d02890bc0d 100644 --- a/test/stringslib.go +++ b/test/stringslib.go @@ -6,7 +6,10 @@ package main -import strings "strings" +import ( + "strconv"; + "strings"; +) func split(s, sep string) *[]string { a := strings.split(s, sep); @@ -31,8 +34,8 @@ func explode(s string) *[]string { } func itoa(i int) string { - s := strings.itoa(i); - n, ok := strings.atoi(s); + s := strconv.itoa(i); + n, ok := strconv.atoi(s); if n != i { print("itoa: ", i, " ", s, "\n"); panic("itoa") @@ -91,17 +94,17 @@ func main() { } { - n, ok := strings.atoi("0"); if n != 0 || !ok { panic("atoi 0") } - n, ok = strings.atoi("-1"); if n != -1 || !ok { panic("atoi -1") } - n, ok = strings.atoi("+345"); if n != 345 || !ok { panic("atoi +345") } - n, ok = strings.atoi("9999"); if n != 9999 || !ok { panic("atoi 9999") } - n, ok = strings.atoi("20ba"); if n != 0 || ok { panic("atoi 20ba") } - n, ok = strings.atoi("hello"); if n != 0 || ok { panic("hello") } + n, ok := strconv.atoi("0"); if n != 0 || !ok { panic("atoi 0") } + n, ok = strconv.atoi("-1"); if n != -1 || !ok { panic("atoi -1") } + n, ok = strconv.atoi("+345"); if n != 345 || !ok { panic("atoi +345") } + n, ok = strconv.atoi("9999"); if n != 9999 || !ok { panic("atoi 9999") } + n, ok = strconv.atoi("20ba"); if n != 0 || ok { panic("atoi 20ba") } + n, ok = strconv.atoi("hello"); if n != 0 || ok { panic("hello") } } - if strings.ftoa(1e6) != "+1.000000e+06" { panic("ftoa 1e6") } - if strings.ftoa(-1e-6) != "-1.000000e-06" { panic("ftoa -1e-6") } - if strings.ftoa(-1.234567e-6) != "-1.234567e-06" { panic("ftoa -1.234567e-6") } + if strconv.ftoa(1e6, 'e', 6) != "1.000000e+06" { panic("ftoa 1e6") } + if strconv.ftoa(-1e-6, 'e', 6) != "-1.000000e-06" { panic("ftoa -1e-6") } + if strconv.ftoa(-1.234567e-6, 'e', 6) != "-1.234567e-06" { panic("ftoa -1.234567e-6") } if itoa(0) != "0" { panic("itoa 0") } if itoa(12345) != "12345" { panic("itoa 12345") } @@ -111,7 +114,7 @@ func main() { // if itoa(-1<<63) != "-9223372036854775808" { panic("itoa 1<<63") } { - a, ok := strings.atof64("-1.2345e4"); + a, overflow, ok := strconv.atof64("-1.2345e4"); if !ok || a != -12345. { panic(a, "atof64 -1.2345e4") } } }