From cf9b7f75349699132332fa597cdbc555ad24ecf7 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Wed, 19 Nov 2008 12:50:34 -0800 Subject: [PATCH] essentially 100% coverage of strconv in tests. fix a few bugs. R=r DELTA=294 (275 added, 9 deleted, 10 changed) OCL=19595 CL=19595 --- src/lib/strconv/atof.go | 25 +++---- src/lib/strconv/decimal.go | 10 ++- src/lib/strconv/ftoa.go | 6 ++ src/lib/strconv/itoa.go | 6 +- src/lib/strconv/testatof.go | 38 +++++++++- src/lib/strconv/testatoi.go | 5 ++ src/lib/strconv/testdecimal.go | 128 +++++++++++++++++++++++++++++++++ src/lib/strconv/testftoa.go | 22 +++++- src/lib/strconv/testitoa.go | 80 +++++++++++++++++++++ 9 files changed, 297 insertions(+), 23 deletions(-) create mode 100644 src/lib/strconv/testdecimal.go create mode 100644 src/lib/strconv/testitoa.go diff --git a/src/lib/strconv/atof.go b/src/lib/strconv/atof.go index 5f019d3ec09..76b5ebacd43 100644 --- a/src/lib/strconv/atof.go +++ b/src/lib/strconv/atof.go @@ -15,6 +15,8 @@ import ( "strconv"; ) +package var optimize = true // can change for testing + // TODO(rsc): Better truncation handling. func StringToDecimal(s string) (neg bool, d *Decimal, trunc bool, ok bool) { i := 0; @@ -182,16 +184,7 @@ func DecimalToFloatBits(neg bool, d *Decimal, trunc bool, flt *FloatInfo) (b uin // Denormalized? if mant&(1<>k == 0; r++ { if r >= a.nd { if n == 0 { + // a == 0; shouldn't get here, but handle anyway. a.nd = 0; return; } - for n >> k == 0 { + for n>>k == 0 { n = n*10; r++; } @@ -276,7 +280,7 @@ func LeftShift(a *Decimal, k uint) { if w != 0 { // TODO: Remove - has no business panicking. - panic("fmt: bad LeftShift"); + panicln("strconv: bad LeftShift", w); } a.nd += delta; a.dp += delta; @@ -287,6 +291,8 @@ func LeftShift(a *Decimal, k uint) { // Returns receiver for convenience. func (a *Decimal) Shift(k int) *Decimal { switch { + case a.nd == 0: + // nothing to do: a == 0 case k > 0: for k > MaxShift { LeftShift(a, MaxShift); diff --git a/src/lib/strconv/ftoa.go b/src/lib/strconv/ftoa.go index f785c85645c..5dd057d494f 100644 --- a/src/lib/strconv/ftoa.go +++ b/src/lib/strconv/ftoa.go @@ -144,6 +144,12 @@ func GenericFtoa(bits uint64, fmt byte, prec int, flt *FloatInfo) string { // 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) { + // If mantissa is zero, the number is zero; stop now. + if mant == 0 { + d.nd = 0; + return; + } + // 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 diff --git a/src/lib/strconv/itoa.go b/src/lib/strconv/itoa.go index 8cac97161d9..6bf26920763 100644 --- a/src/lib/strconv/itoa.go +++ b/src/lib/strconv/itoa.go @@ -8,9 +8,9 @@ export func itoa64(i int64) string { if i == 0 { return "0" } - + neg := false; // negative - u := uint(i); + u := uint64(i); if i < 0 { neg = true; u = -u; @@ -27,7 +27,7 @@ export func itoa64(i int64) string { bp--; b[bp] = '-' } - + // BUG return string(b[bp:len(b)]) return string((&b)[bp:len(b)]) } diff --git a/src/lib/strconv/testatof.go b/src/lib/strconv/testatof.go index 30bc8e953e6..7ec1670be1f 100644 --- a/src/lib/strconv/testatof.go +++ b/src/lib/strconv/testatof.go @@ -16,7 +16,11 @@ type Test struct { } var tests = []Test { + Test{ "", "0", os.EINVAL }, Test{ "1", "1", nil }, + Test{ "+1", "1", nil }, + Test{ "1x", "0", os.EINVAL }, + Test{ "1.1.", "0", os.EINVAL }, Test{ "1e23", "1e+23", nil }, Test{ "100000000000000000000000", "1e+23", nil }, Test{ "1e-100", "1e-100", nil }, @@ -29,6 +33,7 @@ var tests = []Test { Test{ "-1", "-1", nil }, Test{ "-0", "0", nil }, Test{ "1e-20", "1e-20", nil }, + Test{ "625e-3", "0.625", nil }, // largest float64 Test{ "1.7976931348623157e308", "1.7976931348623157e+308", nil }, @@ -85,7 +90,9 @@ var tests = []Test { Test{ ".e-1", "0", os.EINVAL }, } -export func TestAtof() bool { +func XTestAtof(opt bool) bool { + oldopt := strconv.optimize; + strconv.optimize = opt; ok := true; for i := 0; i < len(tests); i++ { t := &tests[i]; @@ -96,6 +103,35 @@ export func TestAtof() bool { t.in, out, err, t.out, t.err); ok = false; } + + if float64(float32(out)) == out { + out32, err := strconv.atof32(t.in); + outs := strconv.ftoa32(out32, 'g', -1); + if outs != t.out || err != t.err { + fmt.printf("strconv.atof32(%v) = %v, %v want %v, %v # %v\n", + t.in, out32, err, t.out, t.err, out); + ok = false; + } + } + + if floatsize == 64 || float64(float32(out)) == out { + outf, err := strconv.atof(t.in); + outs := strconv.ftoa(outf, 'g', -1); + if outs != t.out || err != t.err { + fmt.printf("strconv.ftoa(%v) = %v, %v want %v, %v # %v\n", + t.in, outf, err, t.out, t.err, out); + ok = false; + } + } } + strconv.optimize = oldopt; return ok; } + +export func TestAtof() bool { + return XTestAtof(true); +} + +export func TestAtofSlow() bool { + return XTestAtof(false); +} diff --git a/src/lib/strconv/testatoi.go b/src/lib/strconv/testatoi.go index 7ffd2013809..b318fc79a06 100644 --- a/src/lib/strconv/testatoi.go +++ b/src/lib/strconv/testatoi.go @@ -16,6 +16,7 @@ type Uint64Test struct { } var uint64tests = []Uint64Test { + Uint64Test{ "", 0, os.EINVAL }, Uint64Test{ "0", 0, nil }, Uint64Test{ "1", 1, nil }, Uint64Test{ "12345", 12345, nil }, @@ -24,6 +25,7 @@ var uint64tests = []Uint64Test { Uint64Test{ "98765432100", 98765432100, nil }, Uint64Test{ "18446744073709551615", 1<<64-1, nil }, Uint64Test{ "18446744073709551616", 1<<64-1, os.ERANGE }, + Uint64Test{ "18446744073709551620", 1<<64-1, os.ERANGE }, } type Int64Test struct { @@ -33,6 +35,7 @@ type Int64Test struct { } var int64tests = []Int64Test { + Int64Test{ "", 0, os.EINVAL }, Int64Test{ "0", 0, nil }, Int64Test{ "-0", 0, nil }, Int64Test{ "1", 1, nil }, @@ -60,6 +63,7 @@ type Uint32Test struct { } var uint32tests = []Uint32Test { + Uint32Test{ "", 0, os.EINVAL }, Uint32Test{ "0", 0, nil }, Uint32Test{ "1", 1, nil }, Uint32Test{ "12345", 12345, nil }, @@ -77,6 +81,7 @@ type Int32Test struct { } var int32tests = []Int32Test { + Int32Test{ "", 0, os.EINVAL }, Int32Test{ "0", 0, nil }, Int32Test{ "-0", 0, nil }, Int32Test{ "1", 1, nil }, diff --git a/src/lib/strconv/testdecimal.go b/src/lib/strconv/testdecimal.go new file mode 100644 index 00000000000..767701f159a --- /dev/null +++ b/src/lib/strconv/testdecimal.go @@ -0,0 +1,128 @@ +// 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 + +import ( + "fmt"; + "strconv" +) + +type ShiftTest struct { + i uint64; + shift int; + out string; +} + +var shifttests = []ShiftTest { + ShiftTest{ 0, -100, "0" }, + ShiftTest{ 0, 100, "0" }, + ShiftTest{ 1, 100, "1267650600228229401496703205376" }, + ShiftTest{ 1, -100, + "0.00000000000000000000000000000078886090522101180541" + "17285652827862296732064351090230047702789306640625" }, + ShiftTest{ 12345678, 8, "3160493568" }, + ShiftTest{ 12345678, -8, "48225.3046875" }, + ShiftTest{ 195312, 9, "99999744" }, + ShiftTest{ 1953125, 9, "1000000000" }, +} + +export func TestDecimalShift() bool { + ok := true; + for i := 0; i < len(shifttests); i++ { + t := &shifttests[i]; + s := strconv.NewDecimal(t.i).Shift(t.shift).String(); + if s != t.out { + fmt.printf("Decimal %v << %v = %v, want %v\n", + t.i, t.shift, s, t.out); + ok = false; + } + } + return ok; +} + +type RoundTest struct { + i uint64; + nd int; + down, round, up string; + int uint64; +} + +var roundtests = []RoundTest { + RoundTest{ 0, 4, "0", "0", "0", 0 }, + RoundTest{ 12344999, 4, "12340000", "12340000", "12350000", 12340000 }, + RoundTest{ 12345000, 4, "12340000", "12340000", "12350000", 12340000 }, + RoundTest{ 12345001, 4, "12340000", "12350000", "12350000", 12350000 }, + RoundTest{ 23454999, 4, "23450000", "23450000", "23460000", 23450000 }, + RoundTest{ 23455000, 4, "23450000", "23460000", "23460000", 23460000 }, + RoundTest{ 23455001, 4, "23450000", "23460000", "23460000", 23460000 }, + + RoundTest{ 99994999, 4, "99990000", "99990000", "100000000", 99990000 }, + RoundTest{ 99995000, 4, "99990000", "100000000", "100000000", 100000000 }, + RoundTest{ 99999999, 4, "99990000", "100000000", "100000000", 100000000 }, + + RoundTest{ 12994999, 4, "12990000", "12990000", "13000000", 12990000 }, + RoundTest{ 12995000, 4, "12990000", "13000000", "13000000", 13000000 }, + RoundTest{ 12999999, 4, "12990000", "13000000", "13000000", 13000000 }, +} + +export func TestDecimalRound() bool { + ok := true; + for i := 0; i < len(roundtests); i++ { + t := &roundtests[i]; + s := strconv.NewDecimal(t.i).RoundDown(t.nd).String(); + if s != t.down { + fmt.printf("Decimal %v RoundDown %d = %v, want %v\n", + t.i, t.nd, s, t.down); + ok = false; + } + s = strconv.NewDecimal(t.i).Round(t.nd).String(); + if s != t.round { + fmt.printf("Decimal %v Round %d = %v, want %v\n", + t.i, t.nd, s, t.down); + ok = false; + } + s = strconv.NewDecimal(t.i).RoundUp(t.nd).String(); + if s != t.up { + fmt.printf("Decimal %v RoundUp %d = %v, want %v\n", + t.i, t.nd, s, t.up); + ok = false; + } + } + return ok; +} + +type RoundIntTest struct { + i uint64; + shift int; + int uint64; +} + +var roundinttests = []RoundIntTest { + RoundIntTest{ 0, 100, 0 }, + RoundIntTest{ 512, -8, 2 }, + RoundIntTest{ 513, -8, 2 }, + RoundIntTest{ 640, -8, 2 }, + RoundIntTest{ 641, -8, 3 }, + RoundIntTest{ 384, -8, 2 }, + RoundIntTest{ 385, -8, 2 }, + RoundIntTest{ 383, -8, 1 }, + RoundIntTest{ 1, 100, 1<<64-1 }, + RoundIntTest{ 1000, 0, 1000 }, +} + +export func TestDecimalRoundedInteger() bool { + ok := true; + for i := 0; i < len(roundinttests); i++ { + t := roundinttests[i]; + // TODO: should be able to use int := here. + int1 := strconv.NewDecimal(t.i).Shift(t.shift).RoundedInteger(); + if int1 != t.int { + fmt.printf("Decimal %v >> %v RoundedInteger = %v, want %v\n", + t.i, t.shift, int1, t.int); + ok = false; + } + } + return ok; +} diff --git a/src/lib/strconv/testftoa.go b/src/lib/strconv/testftoa.go index dc4da7ae0d2..390cd8bf572 100644 --- a/src/lib/strconv/testftoa.go +++ b/src/lib/strconv/testftoa.go @@ -12,6 +12,8 @@ type Test struct { s string; } +func fdiv(a, b float64) float64 { return a / b } // keep compiler in the dark + // TODO: Should be able to call this tests but it conflicts with testatof.go var ftests = []Test { Test{ 1, 'e', 5, "1.00000e+00" }, @@ -66,8 +68,22 @@ var ftests = []Test { 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{ fdiv(5e-304, 1e20), 'g', -1, "5e-324" }, + Test{ fdiv(-5e-304, 1e20), 'g', -1, "-5e-324" }, + Test{ 32, 'g', -1, "32" }, + Test{ 32, 'g', 0, "3e+01" }, + + Test{ 100, 'x', -1, "%x" }, + + Test{ sys.NaN(), 'g', -1, "NaN" }, + Test{ -sys.NaN(), 'g', -1, "NaN" }, + Test{ sys.Inf(0), 'g', -1, "+Inf" }, + Test{ sys.Inf(-1), 'g', -1, "-Inf" }, + Test{ -sys.Inf(0), 'g', -1, "-Inf" }, + + Test{ -1, 'b', -1, "-4503599627370496p-52" }, } export func TestFtoa() bool { @@ -82,13 +98,13 @@ export func TestFtoa() bool { println("test", t.f, string(t.fmt), t.prec, "want", t.s, "got", s); ok = false; } - if float64(float32(t.f)) == t.f { + if float64(float32(t.f)) == t.f && t.fmt != 'b' { 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); ok = false; } - } + } } return ok; } diff --git a/src/lib/strconv/testitoa.go b/src/lib/strconv/testitoa.go new file mode 100644 index 00000000000..060264399d2 --- /dev/null +++ b/src/lib/strconv/testitoa.go @@ -0,0 +1,80 @@ +// 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 + +import ( + "fmt"; + "os"; + "strconv"; +) + +type Int64Test struct { + in int64; + out string; +} + +// TODO: should be called int64tests + +var xint64tests = []Int64Test { + Int64Test{ 0, "0" }, + Int64Test{ 1, "1" }, + Int64Test{ -1, "-1" }, + Int64Test{ 12345678, "12345678" }, + Int64Test{ -987654321, "-987654321" }, + Int64Test{ 1<<31-1, "2147483647" }, + Int64Test{ -1<<31+1, "-2147483647" }, + Int64Test{ 1<<31, "2147483648" }, + Int64Test{ -1<<31, "-2147483648" }, + Int64Test{ 1<<31+1, "2147483649" }, + Int64Test{ -1<<31-1, "-2147483649" }, + Int64Test{ 1<<32-1, "4294967295" }, + Int64Test{ -1<<32+1, "-4294967295" }, + Int64Test{ 1<<32, "4294967296" }, + Int64Test{ -1<<32, "-4294967296" }, + Int64Test{ 1<<32+1, "4294967297" }, + Int64Test{ -1<<32-1, "-4294967297" }, + Int64Test{ 1<<50, "1125899906842624" }, + Int64Test{ 1<<63-1, "9223372036854775807" }, + Int64Test{ -1<<63+1, "-9223372036854775807" }, + Int64Test{ -1<<63, "-9223372036854775808" }, +} + +export func TestItoa() bool { + ok := true; + for i := 0; i < len(xint64tests); i++ { + t := xint64tests[i]; + s := strconv.itoa64(t.in); + if s != t.out { + fmt.printf("strconv.itoa64(%v) = %v want %v\n", + t.in, s, t.out); + ok = false; + } + if int64(int(t.in)) == t.in { + s := strconv.itoa(int(t.in)); + if s != t.out { + fmt.printf("strconv.itoa(%v) = %v want %v\n", + t.in, s, t.out); + ok = false; + } + } + } + return ok; +} + +// TODO: Use once there is a strconv.uitoa +type Uint64Test struct { + in uint64; + out string; +} + +// TODO: should be able to call this uint64tests. +var xuint64tests = []Uint64Test { + Uint64Test{ 1<<63-1, "9223372036854775807" }, + Uint64Test{ 1<<63, "9223372036854775808" }, + Uint64Test{ 1<<63+1, "9223372036854775809" }, + Uint64Test{ 1<<64-2, "18446744073709551614" }, + Uint64Test{ 1<<64-1, "18446744073709551615" }, +} +