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