Skip to content

Commit 079c00a

Browse files
committed
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
1 parent f333f46 commit 079c00a

File tree

24 files changed

+1817
-528
lines changed

24 files changed

+1817
-528
lines changed

src/lib/fmt/Makefile

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,17 @@ CC=$(O)c -w
1010
AS=$(O)a
1111
AR=$(O)ar
1212

13-
PKG=$(GOROOT)/pkg/fmt.a
13+
PKG=fmt.a
14+
PKGDIR=$(GOROOT)/pkg
1415

1516
install: $(PKG)
17+
mv $(PKG) $(PKGDIR)/$(PKG)
1618

1719
nuke: clean
18-
rm -f $(PKG)
20+
rm -f $(PKGDIR)/$(PKG)
1921

2022
clean:
21-
rm -f *.$O *.a
23+
rm -f *.$O *.a $(PKG)
2224

2325
%.$O: %.go
2426
$(GC) $*.go
@@ -39,8 +41,10 @@ O2=\
3941
$(PKG): a1 a2
4042
a1: $(O1)
4143
$(AR) grc $(PKG) $(O1)
44+
rm -f $(O1)
4245
a2: $(O2)
4346
$(AR) grc $(PKG) $(O2)
47+
rm -f $(O2)
4448

4549
$(O1): nuke
4650
$(O2): a1

src/lib/fmt/format.go

Lines changed: 51 additions & 194 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
package fmt
66

7+
import "strconv"
8+
79
/*
810
Raw formatter. See print.go for a more palatable interface.
911
@@ -181,7 +183,7 @@ func (f *Fmt) d64(a int64) *Fmt {
181183
f.clearflags();
182184
return f;
183185
}
184-
186+
185187
func (f *Fmt) d32(a int32) *Fmt {
186188
return f.d64(int64(a));
187189
}
@@ -332,227 +334,82 @@ func (f *Fmt) s(s string) *Fmt {
332334
return f;
333335
}
334336

335-
func pow10(n int) float64 {
336-
var d float64;
337-
338-
neg := false;
339-
if n < 0 {
340-
if n < -307 { // DBL_MIN_10_EXP
341-
return 0.;
342-
}
343-
neg = true;
344-
n = -n;
345-
}else if n > 308 { // DBL_MAX_10_EXP
346-
return 1.79769e+308; // HUGE_VAL
347-
}
337+
// floating-point
348338

349-
if n < NPows10 {
350-
d = pows10[n];
351-
} else {
352-
d = pows10[NPows10-1];
353-
for {
354-
n -= NPows10 - 1;
355-
if n < NPows10 {
356-
d *= pows10[n];
357-
break;
358-
}
359-
d *= pows10[NPows10 - 1];
360-
}
361-
}
362-
if neg {
363-
return 1/d;
364-
}
365-
return d;
366-
}
367-
368-
func unpack(a float64) (negative bool, exp int, num float64) {
369-
if a == 0 {
370-
return false, 0, 0.0
371-
}
372-
neg := a < 0;
373-
if neg {
374-
a = -a;
375-
}
376-
// find g,e such that a = g*10^e.
377-
// guess 10-exponent using 2-exponent, then fine tune.
378-
g, e2 := sys.frexp(a);
379-
e := int(float64(e2) * .301029995663981);
380-
g = a * pow10(-e);
381-
for g < 1 {
382-
e--;
383-
g = a * pow10(-e);
384-
}
385-
for g >= 10 {
386-
e++;
387-
g = a * pow10(-e);
388-
}
389-
return neg, e, g;
390-
}
391-
392-
// check for Inf, NaN
393-
func(f *Fmt) InfOrNan(a float64) bool {
394-
if sys.isInf(a, 0) {
395-
if sys.isInf(a, 1) {
396-
f.pad("Inf");
397-
} else {
398-
f.pad("-Inf");
399-
}
400-
f.clearflags();
401-
return true;
402-
}
403-
if sys.isNaN(a) {
404-
f.pad("NaN");
405-
f.clearflags();
406-
return true;
339+
func Prec(f *Fmt, def int) int {
340+
if f.prec_present {
341+
return f.prec;
407342
}
408-
return false;
343+
return def;
409344
}
410345

411-
// float64
412-
func (f *Fmt) e64(a float64) *Fmt {
413-
var negative bool;
414-
var g float64;
415-
var exp int;
416-
if f.InfOrNan(a) {
417-
return f;
418-
}
419-
negative, exp, g = unpack(a);
420-
prec := 6;
421-
if f.prec_present {
422-
prec = f.prec;
423-
}
424-
prec++; // one digit left of decimal
425-
var s string;
426-
// multiply by 10^prec to get decimal places; put decimal after first digit
427-
if g == 0 {
428-
// doesn't work for zero - fake it
429-
s = "000000000000000000000000000000000000000000000000000000000000";
430-
if prec < len(s) {
431-
s = s[0:prec];
432-
} else {
433-
prec = len(s);
434-
}
435-
} else {
436-
g *= pow10(prec);
437-
s = f.integer(int64(g + .5), 10, true, &ldigits); // get the digits into a string
438-
}
439-
s = s[0:1] + "." + s[1:prec]; // insert a decimal point
440-
// print exponent with leading 0 if appropriate.
441-
es := New().p(2).integer(int64(exp), 10, true, &ldigits);
442-
if exp >= 0 {
443-
es = "+" + es; // TODO: should do this with a fmt flag
444-
}
445-
s = s + "e" + es;
446-
if negative {
447-
s = "-" + s;
448-
}
346+
func FmtString(f *Fmt, s string) *Fmt {
449347
f.pad(s);
450348
f.clearflags();
451349
return f;
452350
}
453351

454352
// float64
353+
func (f *Fmt) e64(a float64) *Fmt {
354+
return FmtString(f, strconv.ftoa64(a, 'e', Prec(f, 6)));
355+
}
356+
455357
func (f *Fmt) f64(a float64) *Fmt {
456-
var negative bool;
457-
var g float64;
458-
var exp int;
459-
if f.InfOrNan(a) {
460-
return f;
461-
}
462-
negative, exp, g = unpack(a);
463-
if exp > 19 || exp < -19 { // too big for this sloppy code
464-
return f.e64(a);
465-
}
466-
prec := 6;
467-
if f.prec_present {
468-
prec = f.prec;
469-
}
470-
// prec is number of digits after decimal point
471-
s := "NO";
472-
if exp >= 0 {
473-
g *= pow10(exp);
474-
gi := int64(g);
475-
s = New().integer(gi, 10, true, &ldigits);
476-
s = s + ".";
477-
g -= float64(gi);
478-
s = s + New().p(prec).integer(int64(g*pow10(prec) + .5), 10, true, &ldigits);
479-
} else {
480-
g *= pow10(prec + exp);
481-
s = "0." + New().p(prec).integer(int64(g + .5), 10, true, &ldigits);
482-
}
483-
if negative {
484-
s = "-" + s;
485-
}
486-
f.pad(s);
487-
f.clearflags();
488-
return f;
358+
return FmtString(f, strconv.ftoa64(a, 'f', Prec(f, 6)));
489359
}
490360

491-
// float64
492361
func (f *Fmt) g64(a float64) *Fmt {
493-
if f.InfOrNan(a) {
494-
return f;
495-
}
496-
f1 := New();
497-
f2 := New();
498-
if f.wid_present {
499-
f1.w(f.wid);
500-
f2.w(f.wid);
501-
}
502-
if f.prec_present {
503-
f1.p(f.prec);
504-
f2.p(f.prec);
505-
}
506-
efmt := f1.e64(a).str();
507-
ffmt := f2.f64(a).str();
508-
// ffmt can return e in my bogus world; don't trim trailing 0s if so.
509-
f_is_e := false;
510-
for i := 0; i < len(ffmt); i++ {
511-
if ffmt[i] == 'e' {
512-
f_is_e = true;
513-
break;
514-
}
515-
}
516-
if !f_is_e {
517-
// strip trailing zeros
518-
l := len(ffmt);
519-
for ffmt[l-1]=='0' {
520-
l--;
521-
}
522-
ffmt = ffmt[0:l];
523-
}
524-
if len(efmt) < len(ffmt) {
525-
f.pad(efmt);
526-
} else {
527-
f.pad(ffmt);
528-
}
529-
f.clearflags();
530-
return f;
362+
return FmtString(f, strconv.ftoa64(a, 'g', Prec(f, -1)));
531363
}
532364

533-
// float
534-
func (x *Fmt) f32(a float32) *Fmt {
535-
return x.f64(float64(a))
365+
func (f *Fmt) fb64(a float64) *Fmt {
366+
return FmtString(f, strconv.ftoa64(a, 'b', 0));
536367
}
537368

538-
func (x *Fmt) f(a float) *Fmt {
539-
return x.f64(float64(a))
369+
// float32
370+
// cannot defer to float64 versions
371+
// because it will get rounding wrong in corner cases.
372+
func (f *Fmt) e32(a float32) *Fmt {
373+
return FmtString(f, strconv.ftoa32(a, 'e', Prec(f, -1)));
374+
}
375+
376+
func (f *Fmt) f32(a float32) *Fmt {
377+
return FmtString(f, strconv.ftoa32(a, 'f', Prec(f, 6)));
378+
}
379+
380+
func (f *Fmt) g32(a float32) *Fmt {
381+
return FmtString(f, strconv.ftoa32(a, 'g', Prec(f, -1)));
382+
}
383+
384+
func (f *Fmt) fb32(a float32) *Fmt {
385+
return FmtString(f, strconv.ftoa32(a, 'b', 0));
540386
}
541387

542388
// float
543-
func (x *Fmt) e32(a float32) *Fmt {
544-
return x.e64(float64(a))
389+
func (x *Fmt) f(a float) *Fmt {
390+
if strconv.floatsize == 32 {
391+
return x.f32(float32(a))
392+
}
393+
return x.f64(float64(a))
545394
}
546395

547396
func (x *Fmt) e(a float) *Fmt {
397+
if strconv.floatsize == 32 {
398+
return x.e32(float32(a))
399+
}
548400
return x.e64(float64(a))
549401
}
550402

551-
// float
552-
func (x *Fmt) g32(a float32) *Fmt {
403+
func (x *Fmt) g(a float) *Fmt {
404+
if strconv.floatsize == 32 {
405+
return x.g32(float32(a))
406+
}
553407
return x.g64(float64(a))
554408
}
555409

556-
func (x *Fmt) g(a float) *Fmt {
557-
return x.g64(float64(a))
410+
func (x *Fmt) fb(a float) *Fmt {
411+
if strconv.floatsize == 32 {
412+
return x.fb32(float32(a))
413+
}
414+
return x.fb64(float64(a))
558415
}

0 commit comments

Comments
 (0)