diff options
Diffstat (limited to 'src/lib/strconv')
-rw-r--r-- | src/lib/strconv/Makefile | 58 | ||||
-rw-r--r-- | src/lib/strconv/atof.go | 220 | ||||
-rw-r--r-- | src/lib/strconv/atoi.go | 75 | ||||
-rw-r--r-- | src/lib/strconv/decimal.go | 385 | ||||
-rw-r--r-- | src/lib/strconv/ftoa.go | 379 | ||||
-rw-r--r-- | src/lib/strconv/itoa.go | 38 | ||||
-rwxr-xr-x | src/lib/strconv/test.bash | 20 | ||||
-rw-r--r-- | src/lib/strconv/testatof.go | 46 | ||||
-rw-r--r-- | src/lib/strconv/testfp.go | 156 | ||||
-rw-r--r-- | src/lib/strconv/testfp.txt | 181 | ||||
-rw-r--r-- | src/lib/strconv/testftoa.go | 96 |
11 files changed, 1654 insertions, 0 deletions
diff --git a/src/lib/strconv/Makefile b/src/lib/strconv/Makefile new file mode 100644 index 000000000..e34a0fa92 --- /dev/null +++ b/src/lib/strconv/Makefile @@ -0,0 +1,58 @@ +# Copyright 2009 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m strconv atof.go atoi.go decimal.go ftoa.go itoa.go +O=6 +GC=$(O)g +CC=$(O)c -w +AS=$(O)a +AR=$(O)ar + +PKG=$(GOROOT)/pkg/strconv.a + +install: $(PKG) + +nuke: clean + rm -f $(PKG) + +clean: + rm -f *.$O *.a + +%.$O: %.go + $(GC) $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + + +O1=\ + atoi.$O\ + decimal.$O\ + itoa.$O\ + +O2=\ + ftoa.$O\ + +O3=\ + atof.$O\ + +$(PKG): a1 a2 a3 +a1: $(O1) + $(AR) grc $(PKG) $(O1) + rm -f $(O1) +a2: $(O2) + $(AR) grc $(PKG) $(O2) + rm -f $(O2) +a3: $(O3) + $(AR) grc $(PKG) $(O3) + rm -f $(O3) + +$(O1): nuke +$(O2): a1 +$(O3): a2 + diff --git a/src/lib/strconv/atof.go b/src/lib/strconv/atof.go new file mode 100644 index 000000000..2a34e8d07 --- /dev/null +++ b/src/lib/strconv/atof.go @@ -0,0 +1,220 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Decimal to binary floating point conversion. +// Algorithm: +// 1) Store input in multiprecision decimal. +// 2) Multiply/divide decimal by powers of two until in range [0.5, 1) +// 3) Multiply by 2^precision and round to get mantissa. + +package strconv + +import "strconv" + +// TODO(rsc): Better truncation handling, check for overflow in exponent. +func StringToDecimal(s string) (neg bool, d *Decimal, trunc bool, ok bool) { + i := 0; + + // optional sign + if i >= len(s) { + return; + } + switch { + case s[i] == '+': + i++; + case s[i] == '-': + neg = true; + i++; + } + + // digits + b := new(Decimal); + sawdot := false; + sawdigits := false; + for ; i < len(s); i++ { + switch { + case s[i] == '.': + if sawdot { + return; + } + sawdot = true; + b.dp = b.nd; + continue; + + case '0' <= s[i] && s[i] <= '9': + sawdigits = true; + if s[i] == '0' && b.nd == 0 { // ignore leading zeros + b.dp--; + continue; + } + b.d[b.nd] = s[i]; + b.nd++; + continue; + } + break; + } + if !sawdigits { + return; + } + if !sawdot { + b.dp = b.nd; + } + + // optional exponent moves decimal point + if i < len(s) && s[i] == 'e' { + i++; + if i >= len(s) { + return; + } + esign := 1; + if s[i] == '+' { + i++; + } else if s[i] == '-' { + i++; + esign = -1; + } + if i >= len(s) || s[i] < '0' || s[i] > '9' { + return; + } + e := 0; + for ; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ { + e = e*10 + int(s[i]) - '0'; + } + b.dp += e*esign; + } + + if i != len(s) { + return; + } + + d = b; + ok = true; + return; +} + +// Decimal power of ten to binary power of two. +var powtab = []int{ + 1, 3, 6, 9, 13, 16, 19, 23, 26 +} + +func DecimalToFloatBits(neg bool, d *Decimal, trunc bool, flt *FloatInfo) (b uint64, overflow bool) { + // Zero is always a special case. + if d.nd == 0 { + return 0, false + } + + // TODO: check for obvious overflow + + // Scale by powers of two until in range [0.5, 1.0) + exp := 0; + for d.dp > 0 { + var n int; + if d.dp >= len(powtab) { + n = 27; + } else { + n = powtab[d.dp]; + } + d.Shift(-n); + exp += n; + } + for d.dp < 0 || d.dp == 0 && d.d[0] < '5' { + var n int; + if -d.dp >= len(powtab) { + n = 27; + } else { + n = powtab[-d.dp]; + } + d.Shift(n); + exp -= n; + } + + // Our range is [0.5,1) but floating point range is [1,2). + exp--; + + // Minimum representable exponent is flt.bias+1. + // If the exponent is smaller, move it up and + // adjust d accordingly. + if exp < flt.bias+1 { + n := flt.bias+1 - exp; + d.Shift(-n); + exp += n; + } + + // TODO: overflow/underflow + + // Extract 1+flt.mantbits bits. + mant := d.Shift(int(1+flt.mantbits)).RoundedInteger(); + + // Denormalized? + if mant&(1<<flt.mantbits) == 0 { + if exp != flt.bias+1 { + // TODO: remove - has no business panicking + panicln("DecimalToFloatBits", exp, flt.bias+1); + } + exp--; + } else { + if exp <= flt.bias { + // TODO: remove - has no business panicking + panicln("DecimalToFloatBits1", exp, flt.bias); + } + } + + // Assemble bits. + bits := mant & (uint64(1)<<flt.mantbits - 1); + bits |= uint64((exp-flt.bias)&(1<<flt.expbits - 1)) << flt.mantbits; + if neg { + bits |= 1<<flt.mantbits<<flt.expbits; + } + return bits, false; +} + +// If possible to convert decimal d to 64-bit float f exactly, +// entirely in floating-point math, do so, avoiding the machinery above. +func DecimalToFloat64(neg bool, d *Decimal, trunc bool) (f float64, ok bool) { + // TODO: Fill in. + return 0, false; +} + +// If possible to convert decimal d to 32-bit float f exactly, +// entirely in floating-point math, do so, avoiding the machinery above. +func DecimalToFloat32(neg bool, d *Decimal, trunc bool) (f float32, ok bool) { + // TODO: Fill in. + return 0, false; +} + +export func atof64(s string) (f float64, overflow bool, ok bool) { + neg, d, trunc, ok1 := StringToDecimal(s); + if !ok1 { + return 0, false, false; + } + if f, ok := DecimalToFloat64(neg, d, trunc); ok { + return f, false, true; + } + b, overflow1 := DecimalToFloatBits(neg, d, trunc, &float64info); + return sys.float64frombits(b), overflow1, true; +} + +export func atof32(s string) (f float32, overflow bool, ok bool) { + neg, d, trunc, ok1 := StringToDecimal(s); + if !ok1 { + return 0, false, false; + } + if f, ok := DecimalToFloat32(neg, d, trunc); ok { + return f, false, true; + } + b, overflow1 := DecimalToFloatBits(neg, d, trunc, &float32info); + return sys.float32frombits(uint32(b)), overflow1, true; +} + +export func atof(s string) (f float, overflow bool, ok bool) { + if floatsize == 32 { + var f1 float32; + f1, overflow, ok = atof32(s); + return float(f1), overflow, ok; + } + var f1 float64; + f1, overflow, ok = atof64(s); + return float(f1), overflow, ok; +} + diff --git a/src/lib/strconv/atoi.go b/src/lib/strconv/atoi.go new file mode 100644 index 000000000..7f741c304 --- /dev/null +++ b/src/lib/strconv/atoi.go @@ -0,0 +1,75 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package strconv + +// Convert decimal string to unsigned integer. +// TODO: Doesn't check for overflow. +export func atoui64(s string) (i uint64, ok bool) { + // empty string bad + if len(s) == 0 { + return 0, false + } + + // pick off zero + if s == "0" { + return 0, true + } + + // otherwise, leading zero bad + if s[0] == '0' { + return 0, false + } + + // parse number + n := uint64(0); + for i := 0; i < len(s); i++ { + if s[i] < '0' || s[i] > '9' { + return 0, false + } + n = n*10 + uint64(s[i] - '0') + } + return n, true +} + +// Convert decimal string to integer. +// TODO: Doesn't check for overflow. +export func atoi64(s string) (i int64, ok bool) { + // empty string bad + if len(s) == 0 { + return 0, false + } + + // pick off leading sign + neg := false; + if s[0] == '+' { + s = s[1:len(s)] + } else if s[0] == '-' { + neg = true; + s = s[1:len(s)] + } + + var un uint64; + un, ok = atoui64(s); + if !ok { + return 0, false + } + n := int64(un); + if neg { + n = -n + } + return n, true +} + +export func atoui(s string) (i uint, ok bool) { + ii, okok := atoui64(s); + i = uint(ii); + return i, okok +} + +export func atoi(s string) (i int, ok bool) { + ii, okok := atoi64(s); + i = int(ii); + return i, okok +} diff --git a/src/lib/strconv/decimal.go b/src/lib/strconv/decimal.go new file mode 100644 index 000000000..ee6dd0e78 --- /dev/null +++ b/src/lib/strconv/decimal.go @@ -0,0 +1,385 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Multiprecision decimal numbers. +// For floating-point formatting only; not general purpose. +// Only operations are assign and (binary) left/right shift. +// Can do binary floating point in multiprecision decimal precisely +// because 2 divides 10; cannot do decimal floating point +// in multiprecision binary precisely. + +package strconv + +package type Decimal struct { + // TODO(rsc): Can make d[] a bit smaller and add + // truncated bool; + d [2000] byte; // digits + nd int; // number of digits used + dp int; // decimal point +}; +func (a *Decimal) String() string; +func (a *Decimal) Assign(v uint64); +func (a *Decimal) Shift(k int) *Decimal; +func (a *Decimal) Round(nd int) *Decimal; +func (a *Decimal) RoundUp(nd int) *Decimal; +func (a *Decimal) RoundDown(nd int) *Decimal; +func (a *Decimal) RoundedInteger() uint64; + + +func Copy(dst *[]byte, src *[]byte) int; +func DigitZero(dst *[]byte) int; + +func (a *Decimal) String() string { + n := 10 + a.nd; + if a.dp > 0 { + n += a.dp; + } + if a.dp < 0 { + n += -a.dp; + } + + buf := new([]byte, n); + w := 0; + switch { + case a.dp <= 0: + // zeros fill space between decimal point and digits + buf[w] = '0'; + w++; + buf[w] = '.'; + w++; + w += DigitZero(buf[w:w+-a.dp]); + w += Copy(buf[w:w+a.nd], (&a.d)[0:a.nd]); + + case a.dp < a.nd: + // decimal point in middle of digits + w += Copy(buf[w:w+a.dp], (&a.d)[0:a.dp]); + buf[w] = '.'; + w++; + w += Copy(buf[w:w+a.nd-a.dp], (&a.d)[a.dp:a.nd]); + + default: + // zeros fill space between digits and decimal point + w += Copy(buf[w:w+a.nd], (&a.d)[0:a.nd]); + w += DigitZero(buf[w:w+a.dp-a.nd]); + } + return string(buf[0:w]); +} + +func Copy(dst *[]byte, src *[]byte) int { + for i := 0; i < len(dst); i++ { + dst[i] = src[i]; + } + return len(dst); +} + +func DigitZero(dst *[]byte) int { + for i := 0; i < len(dst); i++ { + dst[i] = '0'; + } + return len(dst); +} + +// Trim trailing zeros from number. +// (They are meaningless; the decimal point is tracked +// independent of the number of digits.) +func Trim(a *Decimal) { + for a.nd > 0 && a.d[a.nd-1] == '0' { + a.nd--; + } + if a.nd == 0 { + a.dp = 0; + } +} + +// Assign v to a. +func (a *Decimal) Assign(v uint64) { + var buf [50]byte; + + // Write reversed decimal in buf. + n := 0; + for v > 0 { + v1 := v/10; + v -= 10*v1; + buf[n] = byte(v + '0'); + n++; + v = v1; + } + + // Reverse again to produce forward decimal in a.d. + a.nd = 0; + for n--; n>=0; n-- { + a.d[a.nd] = buf[n]; + a.nd++; + } + a.dp = a.nd; + Trim(a); +} + +package func NewDecimal(i uint64) *Decimal { + a := new(Decimal); + a.Assign(i); + return a; +} + +// Maximum shift that we can do in one pass without overflow. +// Signed int has 31 bits, and we have to be able to accomodate 9<<k. +const MaxShift = 27 + +// Binary shift right (* 2) by k bits. k <= MaxShift to avoid overflow. +func RightShift(a *Decimal, k uint) { + r := 0; // read pointer + w := 0; // write pointer + + // Pick up enough leading digits to cover first shift. + n := 0; + for ; n>>k == 0; r++ { + if r >= a.nd { + if n == 0 { + a.nd = 0; + return; + } + for n >> k == 0 { + n = n*10; + r++; + } + break; + } + c := int(a.d[r]); + n = n*10 + c-'0'; + } + a.dp -= r-1; + + // Pick up a digit, put down a digit. + for ; r < a.nd; r++ { + c := int(a.d[r]); + dig := n>>k; + n -= dig<<k; + a.d[w] = byte(dig+'0'); + w++; + n = n*10 + c-'0'; + } + + // Put down extra digits. + for n > 0 { + dig := n>>k; + n -= dig<<k; + a.d[w] = byte(dig+'0'); + w++; + n = n*10; + } + + a.nd = w; + Trim(a); +} + +// Cheat sheet for left shift: table indexed by shift count giving +// number of new digits that will be introduced by that shift. +// +// For example, leftcheat[4] = {2, "625"}. That means that +// if we are shifting by 4 (multiplying by 16), it will add 2 digits +// when the string prefix is "625" through "999", and one fewer digit +// if the string prefix is "000" through "624". +// +// Credit for this trick goes to Ken. + +type LeftCheat struct { + delta int; // number of new digits + cutoff string; // minus one digit if original < a. +} + +var leftcheat = []LeftCheat { + // Leading digits of 1/2^i = 5^i. + // 5^23 is not an exact 64-bit floating point number, + // so have to use bc for the math. + /* + seq 27 | sed 's/^/5^/' | bc | + awk 'BEGIN{ print "\tLeftCheat{ 0, \"\" }," } + { + log2 = log(2)/log(10) + printf("\tLeftCheat{ %d, \"%s\" },\t// * %d\n", + int(log2*NR+1), $0, 2**NR) + }' + */ + LeftCheat{ 0, "" }, + LeftCheat{ 1, "5" }, // * 2 + LeftCheat{ 1, "25" }, // * 4 + LeftCheat{ 1, "125" }, // * 8 + LeftCheat{ 2, "625" }, // * 16 + LeftCheat{ 2, "3125" }, // * 32 + LeftCheat{ 2, "15625" }, // * 64 + LeftCheat{ 3, "78125" }, // * 128 + LeftCheat{ 3, "390625" }, // * 256 + LeftCheat{ 3, "1953125" }, // * 512 + LeftCheat{ 4, "9765625" }, // * 1024 + LeftCheat{ 4, "48828125" }, // * 2048 + LeftCheat{ 4, "244140625" }, // * 4096 + LeftCheat{ 4, "1220703125" }, // * 8192 + LeftCheat{ 5, "6103515625" }, // * 16384 + LeftCheat{ 5, "30517578125" }, // * 32768 + LeftCheat{ 5, "152587890625" }, // * 65536 + LeftCheat{ 6, "762939453125" }, // * 131072 + LeftCheat{ 6, "3814697265625" }, // * 262144 + LeftCheat{ 6, "19073486328125" }, // * 524288 + LeftCheat{ 7, "95367431640625" }, // * 1048576 + LeftCheat{ 7, "476837158203125" }, // * 2097152 + LeftCheat{ 7, "2384185791015625" }, // * 4194304 + LeftCheat{ 7, "11920928955078125" }, // * 8388608 + LeftCheat{ 8, "59604644775390625" }, // * 16777216 + LeftCheat{ 8, "298023223876953125" }, // * 33554432 + LeftCheat{ 8, "1490116119384765625" }, // * 67108864 + LeftCheat{ 9, "7450580596923828125" }, // * 134217728 +} + +// Is the leading prefix of b lexicographically less than s? +func PrefixIsLessThan(b *[]byte, s string) bool { + for i := 0; i < len(s); i++ { + if i >= len(b) { + return true; + } + if b[i] != s[i] { + return b[i] < s[i]; + } + } + return false; +} + +// Binary shift left (/ 2) by k bits. k <= MaxShift to avoid overflow. +func LeftShift(a *Decimal, k uint) { + delta := leftcheat[k].delta; + if PrefixIsLessThan((&a.d)[0:a.nd], leftcheat[k].cutoff) { + delta--; + } + + r := a.nd; // read index + w := a.nd + delta; // write index + n := 0; + + // Pick up a digit, put down a digit. + for r--; r >= 0; r-- { + n += (int(a.d[r])-'0') << k; + quo := n/10; + rem := n - 10*quo; + w--; + a.d[w] = byte(rem+'0'); + n = quo; + } + + // Put down extra digits. + for n > 0 { + quo := n/10; + rem := n - 10*quo; + w--; + a.d[w] = byte(rem+'0'); + n = quo; + } + + if w != 0 { + // TODO: Remove - has no business panicking. + panic("fmt: bad LeftShift"); + } + a.nd += delta; + a.dp += delta; + Trim(a); +} + +// Binary shift left (k > 0) or right (k < 0). +// Returns receiver for convenience. +func (a *Decimal) Shift(k int) *Decimal { + switch { + case k > 0: + for k > MaxShift { + LeftShift(a, MaxShift); + k -= MaxShift; + } + LeftShift(a, uint(k)); + case k < 0: + for k < -MaxShift { + RightShift(a, MaxShift); + k += MaxShift; + } + RightShift(a, uint(-k)); + } + return a; +} + +// If we chop a at nd digits, should we round up? +func ShouldRoundUp(a *Decimal, nd int) bool { + if nd <= 0 || nd >= a.nd { + return false; + } + if a.d[nd] == '5' && nd+1 == a.nd { // exactly halfway - round to even + return (a.d[nd-1] - '0') % 2 != 0; + } + // not halfway - digit tells all + return a.d[nd] >= '5'; +} + +// Round a to nd digits (or fewer). +// Returns receiver for convenience. +func (a *Decimal) Round(nd int) *Decimal { + if nd <= 0 || nd >= a.nd { + return a; + } + if(ShouldRoundUp(a, nd)) { + return a.RoundUp(nd); + } + return a.RoundDown(nd); +} + +// Round a down to nd digits (or fewer). +// Returns receiver for convenience. +func (a *Decimal) RoundDown(nd int) *Decimal { + if nd <= 0 || nd >= a.nd { + return a; + } + a.nd = nd; + Trim(a); + return a; +} + +// Round a up to nd digits (or fewer). +// Returns receiver for convenience. +func (a *Decimal) RoundUp(nd int) *Decimal { + if nd <= 0 || nd >= a.nd { + return a; + } + + // round up + for i := nd-1; i >= 0; i-- { + c := a.d[i]; + if c < '9' { // can stop after this digit + a.d[i]++; + a.nd = i+1; + return a; + } + } + + // Number is all 9s. + // Change to single 1 with adjusted decimal point. + a.d[0] = '1'; + a.nd = 1; + a.dp++; + return a; +} + +// Extract integer part, rounded appropriately. +// No guarantees about overflow. +func (a *Decimal) RoundedInteger() uint64 { + if a.dp > 20 { + return 0xFFFFFFFFFFFFFFFF; + } + var i int; + n := uint64(0); + for i = 0; i < a.dp && i < a.nd; i++ { + n = n*10 + uint64(a.d[i] - '0'); + } + for ; i < a.dp; i++ { + n *= 10; + } + if ShouldRoundUp(a, a.dp) { + n++; + } + return n; +} + diff --git a/src/lib/strconv/ftoa.go b/src/lib/strconv/ftoa.go new file mode 100644 index 000000000..f785c8564 --- /dev/null +++ b/src/lib/strconv/ftoa.go @@ -0,0 +1,379 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Binary to decimal floating point conversion. +// Algorithm: +// 1) store mantissa in multiprecision decimal +// 2) shift decimal by exponent +// 3) read digits out & format + +package strconv + +import "strconv" + +// TODO: move elsewhere? +package type FloatInfo struct { + mantbits uint; + expbits uint; + bias int; +} +package var float32info = FloatInfo{ 23, 8, -127 } +package var float64info = FloatInfo{ 52, 11, -1023 } + +func FmtB(neg bool, mant uint64, exp int, flt *FloatInfo) string +func FmtE(neg bool, d *Decimal, prec int) string +func FmtF(neg bool, d *Decimal, prec int) string +func GenericFtoa(bits uint64, fmt byte, prec int, flt *FloatInfo) string +func Max(a, b int) int +func RoundShortest(d *Decimal, mant uint64, exp int, flt *FloatInfo) + +func FloatSize() int { + // Figure out whether float is float32 or float64. + // 1e-35 is representable in both, but 1e-70 + // is too small for a float32. + var f float = 1e-35; + if f*f == 0 { + return 32; + } + return 64; +} +export var floatsize = FloatSize() + +export func ftoa32(f float32, fmt byte, prec int) string { + return GenericFtoa(uint64(sys.float32bits(f)), fmt, prec, &float32info); +} + +export func ftoa64(f float64, fmt byte, prec int) string { + return GenericFtoa(sys.float64bits(f), fmt, prec, &float64info); +} + +export func ftoa(f float, fmt byte, prec int) string { + if floatsize == 32 { + return ftoa32(float32(f), fmt, prec); + } + return ftoa64(float64(f), fmt, prec); +} + +func GenericFtoa(bits uint64, fmt byte, prec int, flt *FloatInfo) string { + neg := bits>>flt.expbits>>flt.mantbits != 0; + exp := int(bits>>flt.mantbits) & (1<<flt.expbits - 1); + mant := bits & (uint64(1)<<flt.mantbits - 1); + + switch exp { + case 1<<flt.expbits - 1: + // Inf, NaN + if mant != 0 { + return "NaN"; + } + if neg { + return "-Inf"; + } + return "+Inf"; + + case 0: + // denormalized + exp++; + + default: + // add implicit top bit + mant |= uint64(1)<<flt.mantbits; + } + exp += flt.bias; + + // Pick off easy binary format. + if fmt == 'b' { + return FmtB(neg, mant, exp, flt); + } + + // Create exact decimal representation. + // The shift is exp - flt.mantbits because mant is a 1-bit integer + // followed by a flt.mantbits fraction, and we are treating it as + // a 1+flt.mantbits-bit integer. + d := NewDecimal(mant).Shift(exp - int(flt.mantbits)); + + // Round appropriately. + // Negative precision means "only as much as needed to be exact." + if prec < 0 { + RoundShortest(d, mant, exp, flt); + switch fmt { + case 'e': + prec = d.nd - 1; + case 'f': + prec = Max(d.nd - d.dp, 0); + case 'g': + prec = d.nd; + } + } else { + switch fmt { + case 'e': + d.Round(prec+1); + case 'f': + d.Round(d.dp+prec); + case 'g': + if prec == 0 { + prec = 1; + } + d.Round(prec); + } + } + + switch fmt { + case 'e': + return FmtE(neg, d, prec); + case 'f': + return FmtF(neg, d, prec); + case 'g': + // trailing zeros are removed. + if prec > d.nd { + prec = d.nd; + } + // %e is used if the exponent from the conversion + // is less than -4 or greater than or equal to the precision. + exp := d.dp - 1; + if exp < -4 || exp >= prec { + return FmtE(neg, d, prec - 1); + } + return FmtF(neg, d, Max(prec - d.dp, 0)); + } + + return "%" + string(fmt); +} + +// Round d (= mant * 2^exp) to the shortest number of digits +// that will let the original floating point value be precisely +// reconstructed. Size is original floating point size (64 or 32). +func RoundShortest(d *Decimal, mant uint64, exp int, flt *FloatInfo) { + // TODO: Unless exp == minexp, if the number of digits in d + // is less than 17, it seems unlikely that it could not be + // the shortest possible number already. So maybe we can + // bail out without doing the extra multiprecision math here. + + // Compute upper and lower such that any decimal number + // between upper and lower (possibly inclusive) + // will round to the original floating point number. + + // d = mant << (exp - mantbits) + // Next highest floating point number is mant+1 << exp-mantbits. + // Our upper bound is halfway inbetween, mant*2+1 << exp-mantbits-1. + upper := NewDecimal(mant*2+1).Shift(exp-int(flt.mantbits)-1); + + // d = mant << (exp - mantbits) + // Next lowest floating point number is mant-1 << exp-mantbits, + // unless mant-1 drops the significant bit and exp is not the minimum exp, + // in which case the next lowest is mant*2-1 << exp-mantbits-1. + // Either way, call it mantlo << explo-mantbits. + // Our lower bound is halfway inbetween, mantlo*2+1 << explo-mantbits-1. + minexp := flt.bias + 1; // minimum possible exponent + var mantlo uint64; + var explo int; + if mant > 1<<flt.mantbits || exp == minexp { + mantlo = mant - 1; + explo = exp; + } else { + mantlo = mant*2-1; + explo = exp-1; + } + lower := NewDecimal(mantlo*2+1).Shift(explo-int(flt.mantbits)-1); + + // The upper and lower bounds are possible outputs only if + // the original mantissa is even, so that IEEE round-to-even + // would round to the original mantissa and not the neighbors. + inclusive := mant%2 == 0; + + // Now we can figure out the minimum number of digits required. + // Walk along until d has distinguished itself from upper and lower. + for i := 0; i < d.nd; i++ { + var l, m, u byte; // lower, middle, upper digits + if i < lower.nd { + l = lower.d[i]; + } else { + l = '0'; + } + m = d.d[i]; + if i < upper.nd { + u = upper.d[i]; + } else { + u = '0'; + } + + // Okay to round down (truncate) if lower has a different digit + // or if lower is inclusive and is exactly the result of rounding down. + okdown := l != m || (inclusive && l == m && i+1 == lower.nd); + + // Okay to round up if upper has a different digit and + // either upper is inclusive or upper is bigger than the result of rounding up. + okup := m != u && (inclusive || i+1 < upper.nd); + + // If it's okay to do either, then round to the nearest one. + // If it's okay to do only one, do it. + switch { + case okdown && okup: + d.Round(i+1); + return; + case okdown: + d.RoundDown(i+1); + return; + case okup: + d.RoundUp(i+1); + return; + } + } +} + +// %e: -d.ddddde±dd +func FmtE(neg bool, d *Decimal, prec int) string { + buf := new([]byte, 3+Max(prec, 0)+30); // "-0." + prec digits + exp + w := 0; // write index + + // sign + if neg { + buf[w] = '-'; + w++; + } + + // first digit + if d.nd == 0 { + buf[w] = '0'; + } else { + buf[w] = d.d[0]; + } + w++; + + // .moredigits + if prec > 0 { + buf[w] = '.'; + w++; + for i := 0; i < prec; i++ { + if 1+i < d.nd { + buf[w] = d.d[1+i]; + } else { + buf[w] = '0'; + } + w++; + } + } + + // e± + buf[w] = 'e'; + w++; + exp := d.dp - 1; + if d.nd == 0 { // special case: 0 has exponent 0 + exp = 0; + } + if exp < 0 { + buf[w] = '-'; + exp = -exp; + } else { + buf[w] = '+'; + } + w++; + + // dddd + // count digits + n := 0; + for e := exp; e > 0; e /= 10 { + n++; + } + // leading zeros + for i := n; i < 2; i++ { + buf[w] = '0'; + w++; + } + // digits + w += n; + n = 0; + for e := exp; e > 0; e /= 10 { + n++; + buf[w-n] = byte(e%10 + '0'); + } + + return string(buf[0:w]); +} + +// %f: -ddddddd.ddddd +func FmtF(neg bool, d *Decimal, prec int) string { + buf := new([]byte, 1+Max(d.dp, 1)+1+Max(prec, 0)); + w := 0; + + // sign + if neg { + buf[w] = '-'; + w++; + } + + // integer, padded with zeros as needed. + if d.dp > 0 { + var i int; + for i = 0; i < d.dp && i < d.nd; i++ { + buf[w] = d.d[i]; + w++; + } + for ; i < d.dp; i++ { + buf[w] = '0'; + w++; + } + } else { + buf[w] = '0'; + w++; + } + + // fraction + if prec > 0 { + buf[w] = '.'; + w++; + for i := 0; i < prec; i++ { + if d.dp+i < 0 || d.dp+i >= d.nd { + buf[w] = '0'; + } else { + buf[w] = d.d[d.dp+i]; + } + w++; + } + } + + return string(buf[0:w]); +} + +// %b: -ddddddddp+ddd +func FmtB(neg bool, mant uint64, exp int, flt *FloatInfo) string { + var buf [50]byte; + w := len(buf); + exp -= int(flt.mantbits); + esign := byte('+'); + if exp < 0 { + esign = '-'; + exp = -exp; + } + n := 0; + for exp > 0 || n < 1 { + n++; + w--; + buf[w] = byte(exp%10 + '0'); + exp /= 10 + } + w--; + buf[w] = esign; + w--; + buf[w] = 'p'; + n = 0; + for mant > 0 || n < 1 { + n++; + w--; + buf[w] = byte(mant%10 + '0'); + mant /= 10; + } + if neg { + w--; + buf[w] = '-'; + } + return string((&buf)[w:len(buf)]); +} + +func Max(a, b int) int { + if a > b { + return a; + } + return b; +} + diff --git a/src/lib/strconv/itoa.go b/src/lib/strconv/itoa.go new file mode 100644 index 000000000..8cac97161 --- /dev/null +++ b/src/lib/strconv/itoa.go @@ -0,0 +1,38 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package strconv + +export func itoa64(i int64) string { + if i == 0 { + return "0" + } + + neg := false; // negative + u := uint(i); + if i < 0 { + neg = true; + u = -u; + } + + // Assemble decimal in reverse order. + var b [32]byte; + bp := len(b); + for ; u > 0; u /= 10 { + bp--; + b[bp] = byte(u%10) + '0' + } + if neg { // add sign + bp--; + b[bp] = '-' + } + + // BUG return string(b[bp:len(b)]) + return string((&b)[bp:len(b)]) +} + +export func itoa(i int) string { + return itoa64(int64(i)); +} + diff --git a/src/lib/strconv/test.bash b/src/lib/strconv/test.bash new file mode 100755 index 000000000..5da777249 --- /dev/null +++ b/src/lib/strconv/test.bash @@ -0,0 +1,20 @@ +#!/bin/bash +# Copyright 2009 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +set -e +set -x + +make clean +make +6g testatof.go +6l testatof.6 +6.out +6g testftoa.go +6l testftoa.6 +6.out +6g testfp.go +6l testfp.6 +6.out +rm -f *.6 6.out diff --git a/src/lib/strconv/testatof.go b/src/lib/strconv/testatof.go new file mode 100644 index 000000000..df3396b8d --- /dev/null +++ b/src/lib/strconv/testatof.go @@ -0,0 +1,46 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "strconv" + +type Test struct { + in string; + out string; +} + +var tests = []Test { + Test{ "1", "1" }, + Test{ "1e23", "1e+23" }, + Test{ "100000000000000000000000", "1e+23" }, + Test{ "1e-100", "1e-100" }, + Test{ "123456700", "1.234567e+08" }, + Test{ "99999999999999974834176", "9.999999999999997e+22" }, + Test{ "100000000000000000000001", "1.0000000000000001e+23" }, + Test{ "100000000000000008388608", "1.0000000000000001e+23" }, + Test{ "100000000000000016777215", "1.0000000000000001e+23" }, + Test{ "100000000000000016777216", "1.0000000000000003e+23" }, + Test{ "-1", "-1" }, + Test{ "-0", "0" }, +} + +func main() { + bad := 0; + for i := 0; i < len(tests); i++ { + t := &tests[i]; + f, overflow, ok := strconv.atof64(t.in); + if !ok { + panicln("test", t.in); + } + s := strconv.ftoa64(f, 'g', -1); + if s != t.out { + println("test", t.in, "want", t.out, "got", s); + bad++; + } + } + if bad != 0 { + panic("failed"); + } +} diff --git a/src/lib/strconv/testfp.go b/src/lib/strconv/testfp.go new file mode 100644 index 000000000..65428b977 --- /dev/null +++ b/src/lib/strconv/testfp.go @@ -0,0 +1,156 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "bufio"; + "fmt"; + "os"; + "strconv"; + "strings"; +) + +func pow2(i int) float64 { + switch { + case i < 0: + return 1 / pow2(-i); + case i == 0: + return 1; + case i == 1: + return 2; + } + return pow2(i/2) * pow2(i-i/2); +} + +// Wrapper around strconv.atof64. Handles dddddp+ddd (binary exponent) +// itself, passes the rest on to strconv.atof64. +func atof64(s string) (f float64, ok bool) { + a := strings.split(s, "p"); + if len(a) == 2 { + n, ok := strconv.atoi64(a[0]); + if !ok { + return 0, false; + } + e, ok1 := strconv.atoi(a[1]); + if !ok1 { + println("bad e", a[1]); + return 0, false; + } + v := float64(n); + // We expect that v*pow2(e) fits in a float64, + // but pow2(e) by itself may not. Be careful. + if e <= -1000 { + v *= pow2(-1000); + e += 1000; + for e < 0 { + v /= 2; + e++; + } + return v, true; + } + if e >= 1000 { + v *= pow2(1000); + e -= 1000; + for e > 0 { + v *= 2; + e--; + } + return v, true; + } + return v*pow2(e), true; + } + f1, overflow, ok1 := strconv.atof64(s); + if !ok1 { + return 0, false; + } + return f1, true; +} + +// Wrapper around strconv.atof32. Handles dddddp+ddd (binary exponent) +// itself, passes the rest on to strconv.atof32. +func atof32(s string) (f float32, ok bool) { + a := strings.split(s, "p"); + if len(a) == 2 { + n, ok := strconv.atoi(a[0]); + if !ok { + println("bad n", a[0]); + return 0, false; + } + e, ok1 := strconv.atoi(a[1]); + if !ok1 { + println("bad p", a[1]); + return 0, false; + } + return float32(float64(n)*pow2(e)), true; + } + f1, overflow, ok1 := strconv.atof32(s); + if !ok1 { + return 0, false; + } + return f1, true; +} + +func main() +{ + fd, err := os.Open("testfp.txt", os.O_RDONLY, 0); + if err != nil { + panicln("testfp: open testfp.txt:", err.String()); + } + + b, err1 := bufio.NewBufRead(fd); + if err1 != nil { + panicln("testfp NewBufRead:", err1.String()); + } + + lineno := 0; + ok := true; + for { + line, err2 := b.ReadLineString('\n', false); + if err2 == bufio.EndOfFile { + break; + } + if err2 != nil { + panicln("testfp: read testfp.txt:", err2.String()); + } + lineno++; + if len(line) == 0 || line[0] == '#' { + continue + } + a := strings.split(line, " "); + if len(a) != 4 { + print("testfp.txt:", lineno, ": wrong field count\n"); + continue; + } + var s string; + var v float64; + switch a[0] { + case "float64": + var ok bool; + v, ok = atof64(a[2]); + if !ok { + print("testfp.txt:", lineno, ": cannot atof64 ", a[2]); + continue; + } + s = fmt.sprintf(a[1], v); + case "float32": + v1, ok := atof32(a[2]); + if !ok { + print("testfp.txt:", lineno, ": cannot atof32 ", a[2]); + continue; + } + s = fmt.sprintf(a[1], v1); + v = float64(v1); + } + if s != a[3] { + print("testfp.txt:", lineno, ": ", a[0], " ", a[1], " ", a[2], " (", v, ") ", + "want ", a[3], " got ", s, "\n"); + ok = false; + } +//else print("testfp.txt:", lineno, ": worked! ", s, "\n"); + } + if !ok { + panicln("testfp failed"); + } +} diff --git a/src/lib/strconv/testfp.txt b/src/lib/strconv/testfp.txt new file mode 100644 index 000000000..08d3c4ef0 --- /dev/null +++ b/src/lib/strconv/testfp.txt @@ -0,0 +1,181 @@ +# Floating-point conversion test cases. +# Empty lines and lines beginning with # are ignored. +# The rest have four fields per line: type, format, input, and output. +# The input is given either in decimal or binary scientific notation. +# The output is the string that should be produced by formatting the +# input with the given format. +# +# The formats are as in C's printf, except that %b means print +# binary scientific notation: NpE = N x 2^E. + +# TODO: +# Powers of 10. +# Powers of 2. +# %.20g versions. +# random sources +# random targets +# random targets ± half a ULP + +# Difficult boundary cases, derived from tables given in +# Vern Paxson, A Program for Testing IEEE Decimal-Binary Conversion +# ftp://ftp.ee.lbl.gov/testbase-report.ps.Z + +# Table 1: Stress Inputs for Conversion to 53-bit Binary, < 1/2 ULP +float64 %b 5e+125 6653062250012735p+365 +float64 %b 69e+267 4705683757438170p+841 +float64 %b 999e-026 6798841691080350p-129 +float64 %b 7861e-034 8975675289889240p-153 +float64 %b 75569e-254 6091718967192243p-880 +float64 %b 928609e-261 7849264900213743p-900 +float64 %b 9210917e+080 8341110837370930p+236 +float64 %b 84863171e+114 4625202867375927p+353 +float64 %b 653777767e+273 5068902999763073p+884 +float64 %b 5232604057e-298 5741343011915040p-1010 +float64 %b 27235667517e-109 6707124626673586p-380 +float64 %b 653532977297e-123 7078246407265384p-422 +float64 %b 3142213164987e-294 8219991337640559p-988 +float64 %b 46202199371337e-072 5224462102115359p-246 +float64 %b 231010996856685e-073 5224462102115359p-247 +float64 %b 9324754620109615e+212 5539753864394442p+705 +float64 %b 78459735791271921e+049 8388176519442766p+166 +float64 %b 272104041512242479e+200 5554409530847367p+670 +float64 %b 6802601037806061975e+198 5554409530847367p+668 +float64 %b 20505426358836677347e-221 4524032052079546p-722 +float64 %b 836168422905420598437e-234 5070963299887562p-760 +float64 %b 4891559871276714924261e+222 6452687840519111p+757 + +# Table 2: Stress Inputs for Conversion to 53-bit Binary, > 1/2 ULP +float64 %b 9e-265 8168427841980010p-930 +float64 %b 85e-037 6360455125664090p-169 +float64 %b 623e+100 6263531988747231p+289 +float64 %b 3571e+263 6234526311072170p+833 +float64 %b 81661e+153 6696636728760206p+472 +float64 %b 920657e-023 5975405561110124p-109 +float64 %b 4603285e-024 5975405561110124p-110 +float64 %b 87575437e-309 8452160731874668p-1053 +float64 %b 245540327e+122 4985336549131723p+381 +float64 %b 6138508175e+120 4985336549131723p+379 +float64 %b 83356057653e+193 5986732817132056p+625 +float64 %b 619534293513e+124 4798406992060657p+399 +float64 %b 2335141086879e+218 5419088166961646p+713 +float64 %b 36167929443327e-159 8135819834632444p-536 +float64 %b 609610927149051e-255 4576664294594737p-850 +float64 %b 3743626360493413e-165 6898586531774201p-549 +float64 %b 94080055902682397e-242 6273271706052298p-800 +float64 %b 899810892172646163e+283 7563892574477827p+947 +float64 %b 7120190517612959703e+120 5385467232557565p+409 +float64 %b 25188282901709339043e-252 5635662608542340p-825 +float64 %b 308984926168550152811e-052 5644774693823803p-157 +float64 %b 6372891218502368041059e+064 4616868614322430p+233 + +# Table 3: Stress Inputs for Converting 53-bit Binary to Decimal, < 1/2 ULP +float64 %.0e 8511030020275656p-342 9e-88 +float64 %.1e 5201988407066741p-824 4.6e-233 +float64 %.2e 6406892948269899p+237 1.41e+87 +float64 %.3e 8431154198732492p+72 3.981e+37 +float64 %.4e 6475049196144587p+99 4.1040e+45 +float64 %.5e 8274307542972842p+726 2.92084e+234 +float64 %.6e 5381065484265332p-456 2.891946e-122 +float64 %.7e 6761728585499734p-1057 4.3787718e-303 +float64 %.8e 7976538478610756p+376 1.22770163e+129 +float64 %.9e 5982403858958067p+377 1.841552452e+129 +float64 %.10e 5536995190630837p+93 5.4835744350e+43 +float64 %.11e 7225450889282194p+710 3.89190181146e+229 +float64 %.12e 7225450889282194p+709 1.945950905732e+229 +float64 %.13e 8703372741147379p+117 1.4460958381605e+51 +float64 %.14e 8944262675275217p-1001 4.17367747458531e-286 +float64 %.15e 7459803696087692p-707 1.107950772878888e-197 +float64 %.16e 6080469016670379p-381 1.2345501366327440e-99 +float64 %.17e 8385515147034757p+721 9.25031711960365024e+232 +float64 %.18e 7514216811389786p-828 4.198047150284889840e-234 +float64 %.19e 8397297803260511p-345 1.1716315319786511046e-88 +float64 %.20e 6733459239310543p+202 4.32810072844612493629e+76 +float64 %.21e 8091450587292794p-473 3.317710118160031081518e-127 + +# Table 4: Stress Inputs for Converting 53-bit Binary to Decimal, > 1/2 ULP +float64 %.0e 6567258882077402p+952 3e+302 +float64 %.1e 6712731423444934p+535 7.6e+176 +float64 %.2e 6712731423444934p+534 3.78e+176 +float64 %.3e 5298405411573037p-957 4.350e-273 +float64 %.4e 5137311167659507p-144 2.3037e-28 +float64 %.5e 6722280709661868p+363 1.26301e+125 +float64 %.6e 5344436398034927p-169 7.142211e-36 +float64 %.7e 8369123604277281p-853 1.3934574e-241 +float64 %.8e 8995822108487663p-780 1.41463449e-219 +float64 %.9e 8942832835564782p-383 4.539277920e-100 +float64 %.10e 8942832835564782p-384 2.2696389598e-100 +float64 %.11e 8942832835564782p-385 1.13481947988e-100 +float64 %.12e 6965949469487146p-249 7.700366561890e-60 +float64 %.13e 6965949469487146p-250 3.8501832809448e-60 +float64 %.14e 6965949469487146p-251 1.92509164047238e-60 +float64 %.15e 7487252720986826p+548 6.898586531774201e+180 +float64 %.16e 5592117679628511p+164 1.3076622631878654e+65 +float64 %.17e 8887055249355788p+665 1.36052020756121240e+216 +float64 %.18e 6994187472632449p+690 3.592810217475959676e+223 +float64 %.19e 8797576579012143p+588 8.9125197712484551899e+192 +float64 %.20e 7363326733505337p+272 5.58769757362301140950e+97 +float64 %.21e 8549497411294502p-448 1.176257830728540379990e-119 + +# Table 14: Stress Inputs for Conversion to 24-bit Binary, <1/2 ULP +# NOTE: The lines with exponent p-149 have been changed from the +# paper. Those entries originally read p-150 and had a mantissa +# twice as large (and even), but IEEE single-precision has no p-150: +# that's the start of the denormals. +float32 %b 5e-20 15474250p-88 +float32 %b 67e+14 12479722p+29 +float32 %b 985e+15 14333636p+36 +# float32 %b 7693e-42 10979816p-150 +float32 %b 7693e-42 5489908p-149 +float32 %b 55895e-16 12888509p-61 +# float32 %b 996622e-44 14224264p-150 +float32 %b 996622e-44 7112132p-149 +float32 %b 7038531e-32 11420669p-107 +# float32 %b 60419369e-46 8623340p-150 +float32 %b 60419369e-46 4311670p-149 +float32 %b 702990899e-20 16209866p-61 +# float32 %b 6930161142e-48 9891056p-150 +float32 %b 6930161142e-48 4945528p-149 +float32 %b 25933168707e+13 14395800p+54 +float32 %b 596428896559e+20 12333860p+82 + +# Table 15: Stress Inputs for Conversion to 24-bit Binary, >1/2 ULP +float32 %b 3e-23 9507380p-98 +float32 %b 57e+18 12960300p+42 +float32 %b 789e-35 10739312p-130 +float32 %b 2539e-18 11990089p-72 +float32 %b 76173e+28 9845130p+86 +float32 %b 887745e-11 9760860p-40 +float32 %b 5382571e-37 11447463p-124 +float32 %b 82381273e-35 8554961p-113 +float32 %b 750486563e-38 9975678p-120 +float32 %b 3752432815e-39 9975678p-121 +float32 %b 75224575729e-45 13105970p-137 +float32 %b 459926601011e+15 12466336p+65 + +# Table 16: Stress Inputs for Converting 24-bit Binary to Decimal, < 1/2 ULP +float32 %.0e 12676506p-102 2e-24 +float32 %.1e 12676506p-103 1.2e-24 +float32 %.2e 15445013p+86 1.19e+33 +float32 %.3e 13734123p-138 3.941e-35 +float32 %.4e 12428269p-130 9.1308e-33 +float32 %.5e 15334037p-146 1.71900e-37 +float32 %.6e 11518287p-41 5.237910e-06 +float32 %.7e 12584953p-145 2.8216440e-37 +float32 %.8e 15961084p-125 3.75243281e-31 +float32 %.9e 14915817p-146 1.672120916e-37 +float32 %.10e 10845484p-102 2.1388945814e-24 +float32 %.11e 16431059p-61 7.12583594561e-12 + +# Table 17: Stress Inputs for Converting 24-bit Binary to Decimal, > 1/2 ULP +float32 %.0e 16093626p+69 1e+28 +float32 %.1e 9983778p+25 3.4e+14 +float32 %.2e 12745034p+104 2.59e+38 +float32 %.3e 12706553p+72 6.001e+28 +float32 %.4e 11005028p+45 3.8721e+20 +float32 %.5e 15059547p+71 3.55584e+28 +float32 %.6e 16015691p-99 2.526831e-23 +float32 %.7e 8667859p+56 6.2458507e+23 +float32 %.8e 14855922p-82 3.07213267e-18 +float32 %.9e 14855922p-83 1.536066333e-18 +float32 %.10e 10144164p-110 7.8147796834e-27 +float32 %.11e 13248074p+95 5.24810279937e+35 diff --git a/src/lib/strconv/testftoa.go b/src/lib/strconv/testftoa.go new file mode 100644 index 000000000..2d72bf42e --- /dev/null +++ b/src/lib/strconv/testftoa.go @@ -0,0 +1,96 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "strconv" + +type Test struct { + f float64; + fmt byte; + prec int; + s string; +} + +var tests = []Test { + Test{ 1, 'e', 5, "1.00000e+00" }, + Test{ 1, 'f', 5, "1.00000" }, + Test{ 1, 'g', 5, "1" }, + Test{ 1, 'g', -1, "1" }, + + Test{ 0, 'e', 5, "0.00000e+00" }, + Test{ 0, 'f', 5, "0.00000" }, + Test{ 0, 'g', 5, "0" }, + Test{ 0, 'g', -1, "0" }, + + Test{ -1, 'e', 5, "-1.00000e+00" }, + Test{ -1, 'f', 5, "-1.00000" }, + Test{ -1, 'g', 5, "-1" }, + Test{ -1, 'g', -1, "-1" }, + + Test{ 12, 'e', 5, "1.20000e+01" }, + Test{ 12, 'f', 5, "12.00000" }, + Test{ 12, 'g', 5, "12" }, + Test{ 12, 'g', -1, "12" }, + + Test{ 123456700, 'e', 5, "1.23457e+08" }, + Test{ 123456700, 'f', 5, "123456700.00000" }, + Test{ 123456700, 'g', 5, "1.2346e+08" }, + Test{ 123456700, 'g', -1, "1.234567e+08" }, + + Test{ 1.2345e6, 'e', 5, "1.23450e+06" }, + Test{ 1.2345e6, 'f', 5, "1234500.00000" }, + Test{ 1.2345e6, 'g', 5, "1.2345e+06" }, + + Test{ 1e23, 'e', 17, "9.99999999999999916e+22" }, + Test{ 1e23, 'f', 17, "99999999999999991611392.00000000000000000" }, + Test{ 1e23, 'g', 17, "9.9999999999999992e+22" }, + + Test{ 1e23, 'e', -1, "1e+23" }, + Test{ 1e23, 'f', -1, "100000000000000000000000" }, + Test{ 1e23, 'g', -1, "1e+23" }, + + Test{ 1e23-8.5e6, 'e', 17, "9.99999999999999748e+22" }, + Test{ 1e23-8.5e6, 'f', 17, "99999999999999974834176.00000000000000000" }, + Test{ 1e23-8.5e6, 'g', 17, "9.9999999999999975e+22" }, + + Test{ 1e23-8.5e6, 'e', -1, "9.999999999999997e+22" }, + Test{ 1e23-8.5e6, 'f', -1, "99999999999999970000000" }, + Test{ 1e23-8.5e6, 'g', -1, "9.999999999999997e+22" }, + + Test{ 1e23+8.5e6, 'e', 17, "1.00000000000000008e+23" }, + Test{ 1e23+8.5e6, 'f', 17, "100000000000000008388608.00000000000000000" }, + Test{ 1e23+8.5e6, 'g', 17, "1.0000000000000001e+23" }, + + Test{ 1e23+8.5e6, 'e', -1, "1.0000000000000001e+23" }, + Test{ 1e23+8.5e6, 'f', -1, "100000000000000010000000" }, + Test{ 1e23+8.5e6, 'g', -1, "1.0000000000000001e+23" }, + + Test{ 32, 'g', -1, "32" }, +} + +func main() { + bad := 0; + if strconv.floatsize != 32 { + panic("floatsize: ", strconv.floatsize); + } + for i := 0; i < len(tests); i++ { + t := &tests[i]; + s := strconv.ftoa64(t.f, t.fmt, t.prec); + if s != t.s { + println("test", t.f, string(t.fmt), t.prec, "want", t.s, "got", s); + bad++; + } + if float64(float32(t.f)) == t.f { + s := strconv.ftoa32(float32(t.f), t.fmt, t.prec); + if s != t.s { + println("test32", t.f, string(t.fmt), t.prec, "want", t.s, "got", s); + bad++; + } + } + } + if bad != 0 { + panic("failed"); + } +} |