mirror of
https://github.com/golang/go
synced 2024-11-12 06:30:21 -07:00
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
This commit is contained in:
parent
f333f4685c
commit
079c00a475
@ -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
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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");
|
||||
}
|
||||
|
||||
|
@ -31,10 +31,11 @@ rm -f *.6
|
||||
|
||||
buildfiles strings.go
|
||||
|
||||
builddirs syscall \
|
||||
math \
|
||||
os \
|
||||
reflect \
|
||||
builddirs syscall\
|
||||
math\
|
||||
os\
|
||||
strconv\
|
||||
reflect\
|
||||
|
||||
buildfiles io.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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
58
src/lib/strconv/Makefile
Normal file
58
src/lib/strconv/Makefile
Normal file
@ -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
|
||||
|
220
src/lib/strconv/atof.go
Normal file
220
src/lib/strconv/atof.go
Normal file
@ -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;
|
||||
}
|
||||
|
75
src/lib/strconv/atoi.go
Normal file
75
src/lib/strconv/atoi.go
Normal file
@ -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
|
||||
}
|
385
src/lib/strconv/decimal.go
Normal file
385
src/lib/strconv/decimal.go
Normal file
@ -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;
|
||||
}
|
||||
|
379
src/lib/strconv/ftoa.go
Normal file
379
src/lib/strconv/ftoa.go
Normal file
@ -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;
|
||||
}
|
||||
|
38
src/lib/strconv/itoa.go
Normal file
38
src/lib/strconv/itoa.go
Normal file
@ -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));
|
||||
}
|
||||
|
20
src/lib/strconv/test.bash
Executable file
20
src/lib/strconv/test.bash
Executable file
@ -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
|
46
src/lib/strconv/testatof.go
Normal file
46
src/lib/strconv/testatof.go
Normal file
@ -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");
|
||||
}
|
||||
}
|
156
src/lib/strconv/testfp.go
Normal file
156
src/lib/strconv/testfp.go
Normal file
@ -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");
|
||||
}
|
||||
}
|
181
src/lib/strconv/testfp.txt
Normal file
181
src/lib/strconv/testfp.txt
Normal file
@ -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
|
96
src/lib/strconv/testftoa.go
Normal file
96
src/lib/strconv/testftoa.go
Normal file
@ -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");
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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|");
|
||||
}
|
||||
|
@ -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") }
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user