correctly rounded floating-point conversions
in new package strconv.

move atoi etc to strconv too.

update fmt, etc to use strconv.

R=r
DELTA=2232  (1691 added, 424 deleted, 117 changed)
OCL=19286
CL=19380
diff --git a/src/lib/fmt/Makefile b/src/lib/fmt/Makefile
index 64ca60c..b9148cc 100644
--- a/src/lib/fmt/Makefile
+++ b/src/lib/fmt/Makefile
@@ -10,15 +10,17 @@
 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 @@
 $(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 089de43..058c619 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 @@
 	f.clearflags();
 	return f;
 }
-	
+
 func (f *Fmt) d32(a int32) *Fmt {
 	return f.d64(int64(a));
 }
@@ -332,227 +334,82 @@
 	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 8fa337f..ce7a4f2 100644
--- a/src/lib/fmt/print.go
+++ b/src/lib/fmt/print.go
@@ -230,12 +230,24 @@
 	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 @@
 	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 @@
 			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 @@
 
 			// 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 43b4fb1..7aef824 100644
--- a/src/lib/http/server.go
+++ b/src/lib/http/server.go
@@ -13,7 +13,7 @@
 	"os";
 	"net";
 	"http";
-	"strings"
+	"strconv";
 )
 
 // Serve a new connection.
@@ -39,7 +39,7 @@
 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 820fdbf..b255b08 100755
--- a/src/lib/make.bash
+++ b/src/lib/make.bash
@@ -31,10 +31,11 @@
 
 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 49d9c1b..5db5909 100644
--- a/src/lib/net/net.go
+++ b/src/lib/net/net.go
@@ -7,8 +7,8 @@
 import (
 	"os";
 	"net";
-	"strings";
-	"syscall"
+	"strconv";
+	"syscall";
 )
 
 export var (
@@ -113,7 +113,7 @@
 			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 09b3b68..8497380 100644
--- a/src/lib/reflect/test.go
+++ b/src/lib/reflect/test.go
@@ -72,9 +72,9 @@
 	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 @@
 	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 @@
 	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 @@
 	}
 	{	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 @@
 	}
 	{
 		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 0a7004b..f33f5272 100644
--- a/src/lib/reflect/tostring.go
+++ b/src/lib/reflect/tostring.go
@@ -9,7 +9,7 @@
 
 import (
 	"reflect";
-	"strings";
+	"strconv";
 )
 
 export func TypeToString(typ Type, expand bool) string
@@ -81,7 +81,7 @@
 		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 @@
 
 // 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 @@
 	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 0000000..e34a0fa
--- /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 0000000..2a34e8d
--- /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<<flt.mantbits) == 0 {
+		if exp != flt.bias+1 {
+			// TODO: remove - has no business panicking
+			panicln("DecimalToFloatBits", exp, flt.bias+1);
+		}
+		exp--;
+	} else {
+		if exp <= flt.bias {
+			// TODO: remove - has no business panicking
+			panicln("DecimalToFloatBits1", exp, flt.bias);
+		}
+	}
+
+	// Assemble bits.
+	bits := mant & (uint64(1)<<flt.mantbits - 1);
+	bits |= uint64((exp-flt.bias)&(1<<flt.expbits - 1)) << flt.mantbits;
+	if neg {
+		bits |= 1<<flt.mantbits<<flt.expbits;
+	}
+	return bits, false;
+}
+
+// If possible to convert decimal d to 64-bit float f exactly,
+// entirely in floating-point math, do so, avoiding the machinery above.
+func DecimalToFloat64(neg bool, d *Decimal, trunc bool) (f float64, ok bool) {
+	// TODO: Fill in.
+	return 0, false;
+}
+
+// If possible to convert decimal d to 32-bit float f exactly,
+// entirely in floating-point math, do so, avoiding the machinery above.
+func DecimalToFloat32(neg bool, d *Decimal, trunc bool) (f float32, ok bool) {
+	// TODO: Fill in.
+	return 0, false;
+}
+
+export func atof64(s string) (f float64, overflow bool, ok bool) {
+	neg, d, trunc, ok1 := StringToDecimal(s);
+	if !ok1 {
+		return 0, false, false;
+	}
+	if f, ok := DecimalToFloat64(neg, d, trunc); ok {
+		return f, false, true;
+	}
+	b, overflow1 := DecimalToFloatBits(neg, d, trunc, &float64info);
+	return sys.float64frombits(b), overflow1, true;
+}
+
+export func atof32(s string) (f float32, overflow bool, ok bool) {
+	neg, d, trunc, ok1 := StringToDecimal(s);
+	if !ok1 {
+		return 0, false, false;
+	}
+	if f, ok := DecimalToFloat32(neg, d, trunc); ok {
+		return f, false, true;
+	}
+	b, overflow1 := DecimalToFloatBits(neg, d, trunc, &float32info);
+	return sys.float32frombits(uint32(b)), overflow1, true;
+}
+
+export func atof(s string) (f float, overflow bool, ok bool) {
+	if floatsize == 32 {
+		var f1 float32;
+		f1, overflow, ok = atof32(s);
+		return float(f1), overflow, ok;
+	}
+	var f1 float64;
+	f1, overflow, ok = atof64(s);
+	return float(f1), overflow, ok;
+}
+
diff --git a/src/lib/strconv/atoi.go b/src/lib/strconv/atoi.go
new file mode 100644
index 0000000..7f741c3
--- /dev/null
+++ b/src/lib/strconv/atoi.go
@@ -0,0 +1,75 @@
+// 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
+
+// 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
+}
diff --git a/src/lib/strconv/decimal.go b/src/lib/strconv/decimal.go
new file mode 100644
index 0000000..ee6dd0e
--- /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.
+const MaxShift = 27
+
+// Binary shift right (* 2) by k bits.  k <= MaxShift to avoid overflow.
+func RightShift(a *Decimal, k uint) {
+	r := 0;	// read pointer
+	w := 0;	// write pointer
+
+	// Pick up enough leading digits to cover first shift.
+	n := 0;
+	for ; n>>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<<k;
+		a.d[w] = byte(dig+'0');
+		w++;
+		n = n*10 + c-'0';
+	}
+
+	// Put down extra digits.
+	for n > 0 {
+		dig := n>>k;
+		n -= dig<<k;
+		a.d[w] = byte(dig+'0');
+		w++;
+		n = n*10;
+	}
+
+	a.nd = w;
+	Trim(a);
+}
+
+// Cheat sheet for left shift: table indexed by shift count giving
+// number of new digits that will be introduced by that shift.
+//
+// For example, leftcheat[4] = {2, "625"}.  That means that
+// if we are shifting by 4 (multiplying by 16), it will add 2 digits
+// when the string prefix is "625" through "999", and one fewer digit
+// if the string prefix is "000" through "624".
+//
+// Credit for this trick goes to Ken.
+
+type LeftCheat struct {
+	delta int;	// number of new digits
+	cutoff string;	//   minus one digit if original < a.
+}
+
+var leftcheat = []LeftCheat {
+	// Leading digits of 1/2^i = 5^i.
+	// 5^23 is not an exact 64-bit floating point number,
+	// so have to use bc for the math.
+	/*
+	seq 27 | sed 's/^/5^/' | bc |
+	awk 'BEGIN{ print "\tLeftCheat{ 0, \"\" }," }
+	{
+		log2 = log(2)/log(10)
+		printf("\tLeftCheat{ %d, \"%s\" },\t// * %d\n",
+			int(log2*NR+1), $0, 2**NR)
+	}'
+	 */
+	LeftCheat{ 0, "" },
+	LeftCheat{ 1, "5" },	// * 2
+	LeftCheat{ 1, "25" },	// * 4
+	LeftCheat{ 1, "125" },	// * 8
+	LeftCheat{ 2, "625" },	// * 16
+	LeftCheat{ 2, "3125" },	// * 32
+	LeftCheat{ 2, "15625" },	// * 64
+	LeftCheat{ 3, "78125" },	// * 128
+	LeftCheat{ 3, "390625" },	// * 256
+	LeftCheat{ 3, "1953125" },	// * 512
+	LeftCheat{ 4, "9765625" },	// * 1024
+	LeftCheat{ 4, "48828125" },	// * 2048
+	LeftCheat{ 4, "244140625" },	// * 4096
+	LeftCheat{ 4, "1220703125" },	// * 8192
+	LeftCheat{ 5, "6103515625" },	// * 16384
+	LeftCheat{ 5, "30517578125" },	// * 32768
+	LeftCheat{ 5, "152587890625" },	// * 65536
+	LeftCheat{ 6, "762939453125" },	// * 131072
+	LeftCheat{ 6, "3814697265625" },	// * 262144
+	LeftCheat{ 6, "19073486328125" },	// * 524288
+	LeftCheat{ 7, "95367431640625" },	// * 1048576
+	LeftCheat{ 7, "476837158203125" },	// * 2097152
+	LeftCheat{ 7, "2384185791015625" },	// * 4194304
+	LeftCheat{ 7, "11920928955078125" },	// * 8388608
+	LeftCheat{ 8, "59604644775390625" },	// * 16777216
+	LeftCheat{ 8, "298023223876953125" },	// * 33554432
+	LeftCheat{ 8, "1490116119384765625" },	// * 67108864
+	LeftCheat{ 9, "7450580596923828125" },	// * 134217728
+}
+
+// Is the leading prefix of b lexicographically less than s?
+func PrefixIsLessThan(b *[]byte, s string) bool {
+	for i := 0; i < len(s); i++ {
+		if i >= 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 0000000..f785c85
--- /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<<flt.expbits - 1);
+	mant := bits & (uint64(1)<<flt.mantbits - 1);
+
+	switch exp {
+	case 1<<flt.expbits - 1:
+		// Inf, NaN
+		if mant != 0 {
+			return "NaN";
+		}
+		if neg {
+			return "-Inf";
+		}
+		return "+Inf";
+
+	case 0:
+		// denormalized
+		exp++;
+
+	default:
+		// add implicit top bit
+		mant |= uint64(1)<<flt.mantbits;
+	}
+	exp += flt.bias;
+
+	// Pick off easy binary format.
+	if fmt == 'b' {
+		return FmtB(neg, mant, exp, flt);
+	}
+
+	// Create exact decimal representation.
+	// The shift is exp - flt.mantbits because mant is a 1-bit integer
+	// followed by a flt.mantbits fraction, and we are treating it as
+	// a 1+flt.mantbits-bit integer.
+	d := NewDecimal(mant).Shift(exp - int(flt.mantbits));
+
+	// Round appropriately.
+	// Negative precision means "only as much as needed to be exact."
+	if prec < 0 {
+		RoundShortest(d, mant, exp, flt);
+		switch fmt {
+		case 'e':
+			prec = d.nd - 1;
+		case 'f':
+			prec = Max(d.nd - d.dp, 0);
+		case 'g':
+			prec = d.nd;
+		}
+	} else {
+		switch fmt {
+		case 'e':
+			d.Round(prec+1);
+		case 'f':
+			d.Round(d.dp+prec);
+		case 'g':
+			if prec == 0 {
+				prec = 1;
+			}
+			d.Round(prec);
+		}
+	}
+
+	switch fmt {
+	case 'e':
+		return FmtE(neg, d, prec);
+	case 'f':
+		return FmtF(neg, d, prec);
+	case 'g':
+		// trailing zeros are removed.
+		if prec > 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<<flt.mantbits || exp == minexp {
+		mantlo = mant - 1;
+		explo = exp;
+	} else {
+		mantlo = mant*2-1;
+		explo = exp-1;
+	}
+	lower := NewDecimal(mantlo*2+1).Shift(explo-int(flt.mantbits)-1);
+
+	// The upper and lower bounds are possible outputs only if
+	// the original mantissa is even, so that IEEE round-to-even
+	// would round to the original mantissa and not the neighbors.
+	inclusive := mant%2 == 0;
+
+	// Now we can figure out the minimum number of digits required.
+	// Walk along until d has distinguished itself from upper and lower.
+	for i := 0; i < d.nd; i++ {
+		var l, m, u byte;	// lower, middle, upper digits
+		if i < lower.nd {
+			l = lower.d[i];
+		} else {
+			l = '0';
+		}
+		m = d.d[i];
+		if i < upper.nd {
+			u = upper.d[i];
+		} else {
+			u = '0';
+		}
+
+		// Okay to round down (truncate) if lower has a different digit
+		// or if lower is inclusive and is exactly the result of rounding down.
+		okdown := l != m || (inclusive && l == m && i+1 == lower.nd);
+
+		// Okay to round up if upper has a different digit and
+		// either upper is inclusive or upper is bigger than the result of rounding up.
+		okup := m != u && (inclusive || i+1 < upper.nd);
+
+		// If it's okay to do either, then round to the nearest one.
+		// If it's okay to do only one, do it.
+		switch {
+		case okdown && okup:
+			d.Round(i+1);
+			return;
+		case okdown:
+			d.RoundDown(i+1);
+			return;
+		case okup:
+			d.RoundUp(i+1);
+			return;
+		}
+	}
+}
+
+// %e: -d.ddddde±dd
+func FmtE(neg bool, d *Decimal, prec int) string {
+	buf := new([]byte, 3+Max(prec, 0)+30);	// "-0." + prec digits + exp
+	w := 0;	// write index
+
+	// sign
+	if neg {
+		buf[w] = '-';
+		w++;
+	}
+
+	// first digit
+	if d.nd == 0 {
+		buf[w] = '0';
+	} else {
+		buf[w] = d.d[0];
+	}
+	w++;
+
+	// .moredigits
+	if prec > 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 0000000..8cac971
--- /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 0000000..5da7772
--- /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 0000000..df3396b
--- /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 0000000..65428b9
--- /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 0000000..08d3c4e
--- /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 0000000..2d72bf4
--- /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 6953922..433e500 100644
--- a/src/lib/strings.go
+++ b/src/lib/strings.go
@@ -115,272 +115,3 @@
 	}
 	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 d412a69..c554636 100755
--- a/src/run.bash
+++ b/src/run.bash
@@ -11,6 +11,12 @@
 	builtin cd $1
 }
 
+(xcd lib/strconv
+make clean
+time make
+bash test.bash
+) || exit $?
+
 (xcd lib/reflect
 make clean
 time make