Commit efbeaedb authored by Russ Cox's avatar Russ Cox

strconv: new API

R=golang-dev, bradfitz, gri, r, agl
CC=golang-dev
https://golang.org/cl/5434095
parent 40b2fe00
...@@ -4,10 +4,10 @@ ...@@ -4,10 +4,10 @@
package strconv package strconv
// Atob returns the boolean value represented by the string. // ParseBool returns the boolean value represented by the string.
// It accepts 1, t, T, TRUE, true, True, 0, f, F, FALSE, false, False. // It accepts 1, t, T, TRUE, true, True, 0, f, F, FALSE, false, False.
// Any other value returns an error. // Any other value returns an error.
func Atob(str string) (value bool, err error) { func ParseBool(str string) (value bool, err error) {
switch str { switch str {
case "1", "t", "T", "true", "TRUE", "True": case "1", "t", "T", "true", "TRUE", "True":
return true, nil return true, nil
...@@ -17,10 +17,19 @@ func Atob(str string) (value bool, err error) { ...@@ -17,10 +17,19 @@ func Atob(str string) (value bool, err error) {
return false, &NumError{str, ErrSyntax} return false, &NumError{str, ErrSyntax}
} }
// Btoa returns "true" or "false" according to the value of the boolean argument // FormatBool returns "true" or "false" according to the value of b
func Btoa(b bool) string { func FormatBool(b bool) string {
if b { if b {
return "true" return "true"
} }
return "false" return "false"
} }
// AppendBool appends "true" or "false", according to the value of b,
// to dst and returns the extended buffer.
func AppendBool(dst []byte, b bool) []byte {
if b {
return append(dst, "true"...)
}
return append(dst, "false"...)
}
...@@ -32,9 +32,9 @@ var atobtests = []atobTest{ ...@@ -32,9 +32,9 @@ var atobtests = []atobTest{
{"True", true, nil}, {"True", true, nil},
} }
func TestAtob(t *testing.T) { func TestParseBool(t *testing.T) {
for _, test := range atobtests { for _, test := range atobtests {
b, e := Atob(test.in) b, e := ParseBool(test.in)
if test.err != nil { if test.err != nil {
// expect an error // expect an error
if e == nil { if e == nil {
......
...@@ -338,21 +338,7 @@ func (d *decimal) atof32() (f float32, ok bool) { ...@@ -338,21 +338,7 @@ func (d *decimal) atof32() (f float32, ok bool) {
return return
} }
// Atof32 converts the string s to a 32-bit floating-point number. func atof32(s string) (f float32, err error) {
//
// If s is well-formed and near a valid floating point number,
// Atof32 returns the nearest floating point number rounded
// using IEEE754 unbiased rounding.
//
// The errors that Atof32 returns have concrete type *NumError
// and include err.Num = s.
//
// If s is not syntactically well-formed, Atof32 returns err.Error = ErrSyntax.
//
// If s is syntactically well-formed but is more than 1/2 ULP
// away from the largest floating point number of the given size,
// Atof32 returns f = ±Inf, err.Error = ErrRange.
func Atof32(s string) (f float32, err error) {
if val, ok := special(s); ok { if val, ok := special(s); ok {
return float32(val), nil return float32(val), nil
} }
...@@ -374,10 +360,7 @@ func Atof32(s string) (f float32, err error) { ...@@ -374,10 +360,7 @@ func Atof32(s string) (f float32, err error) {
return f, err return f, err
} }
// Atof64 converts the string s to a 64-bit floating-point number. func atof64(s string) (f float64, err error) {
// Except for the type of its result, its definition is the same as that
// of Atof32.
func Atof64(s string) (f float64, err error) {
if val, ok := special(s); ok { if val, ok := special(s); ok {
return val, nil return val, nil
} }
...@@ -399,14 +382,28 @@ func Atof64(s string) (f float64, err error) { ...@@ -399,14 +382,28 @@ func Atof64(s string) (f float64, err error) {
return f, err return f, err
} }
// AtofN converts the string s to a 64-bit floating-point number, // ParseFloat converts the string s to a floating-point number
// but it rounds the result assuming that it will be stored in a value // with the precision specified by bitSize: 32 for float32, or 64 for float64.
// of n bits (32 or 64). // When bitSize=32, the result still has type float64, but it will be
func AtofN(s string, n int) (f float64, err error) { // convertible to float32 without changing its value.
if n == 32 { //
f1, err1 := Atof32(s) // If s is well-formed and near a valid floating point number,
// ParseFloat returns the nearest floating point number rounded
// using IEEE754 unbiased rounding.
//
// The errors that ParseFloat returns have concrete type *NumError
// and include err.Num = s.
//
// If s is not syntactically well-formed, ParseFloat returns err.Error = ErrSyntax.
//
// If s is syntactically well-formed but is more than 1/2 ULP
// away from the largest floating point number of the given size,
// ParseFloat returns f = ±Inf, err.Error = ErrRange.
func ParseFloat(s string, bitSize int) (f float64, err error) {
if bitSize == 32 {
f1, err1 := atof32(s)
return float64(f1), err1 return float64(f1), err1
} }
f1, err1 := Atof64(s) f1, err1 := atof64(s)
return f1, err1 return f1, err1
} }
...@@ -128,33 +128,23 @@ func testAtof(t *testing.T, opt bool) { ...@@ -128,33 +128,23 @@ func testAtof(t *testing.T, opt bool) {
oldopt := SetOptimize(opt) oldopt := SetOptimize(opt)
for i := 0; i < len(atoftests); i++ { for i := 0; i < len(atoftests); i++ {
test := &atoftests[i] test := &atoftests[i]
out, err := Atof64(test.in) out, err := ParseFloat(test.in, 64)
outs := Ftoa64(out, 'g', -1) outs := FormatFloat(out, 'g', -1, 64)
if outs != test.out || !reflect.DeepEqual(err, test.err) { if outs != test.out || !reflect.DeepEqual(err, test.err) {
t.Errorf("Atof64(%v) = %v, %v want %v, %v", t.Errorf("ParseFloat(%v, 64) = %v, %v want %v, %v",
test.in, out, err, test.out, test.err)
}
out, err = AtofN(test.in, 64)
outs = FtoaN(out, 'g', -1, 64)
if outs != test.out || !reflect.DeepEqual(err, test.err) {
t.Errorf("AtofN(%v, 64) = %v, %v want %v, %v",
test.in, out, err, test.out, test.err) test.in, out, err, test.out, test.err)
} }
if float64(float32(out)) == out { if float64(float32(out)) == out {
out32, err := Atof32(test.in) out, err := ParseFloat(test.in, 32)
outs := Ftoa32(out32, 'g', -1) out32 := float32(out)
if outs != test.out || !reflect.DeepEqual(err, test.err) { if float64(out32) != out {
t.Errorf("Atof32(%v) = %v, %v want %v, %v # %v", t.Errorf("ParseFloat(%v, 32) = %v, not a float32 (closest is %v)", test.in, out, float64(out32))
test.in, out32, err, test.out, test.err, out) continue
} }
outs := FormatFloat(float64(out32), 'g', -1, 32)
out, err := AtofN(test.in, 32)
out32 = float32(out)
outs = FtoaN(float64(out32), 'g', -1, 32)
if outs != test.out || !reflect.DeepEqual(err, test.err) { if outs != test.out || !reflect.DeepEqual(err, test.err) {
t.Errorf("AtofN(%v, 32) = %v, %v want %v, %v # %v", t.Errorf("ParseFloat(%v, 32) = %v, %v want %v, %v # %v",
test.in, out32, err, test.out, test.err, out) test.in, out32, err, test.out, test.err, out)
} }
} }
...@@ -168,24 +158,24 @@ func TestAtofSlow(t *testing.T) { testAtof(t, false) } ...@@ -168,24 +158,24 @@ func TestAtofSlow(t *testing.T) { testAtof(t, false) }
func BenchmarkAtof64Decimal(b *testing.B) { func BenchmarkAtof64Decimal(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
Atof64("33909") ParseFloat("33909", 64)
} }
} }
func BenchmarkAtof64Float(b *testing.B) { func BenchmarkAtof64Float(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
Atof64("339.7784") ParseFloat("339.7784", 64)
} }
} }
func BenchmarkAtof64FloatExp(b *testing.B) { func BenchmarkAtof64FloatExp(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
Atof64("-5.09e75") ParseFloat("-5.09e75", 64)
} }
} }
func BenchmarkAtof64Big(b *testing.B) { func BenchmarkAtof64Big(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
Atof64("123456789123456789123456789") ParseFloat("123456789123456789123456789", 64)
} }
} }
...@@ -20,15 +20,9 @@ type NumError struct { ...@@ -20,15 +20,9 @@ type NumError struct {
func (e *NumError) Error() string { return `parsing "` + e.Num + `": ` + e.Err.Error() } func (e *NumError) Error() string { return `parsing "` + e.Num + `": ` + e.Err.Error() }
func computeIntsize() uint { const intSize = 32 << uint(^uint(0)>>63)
siz := uint(8)
for 1<<siz != 0 {
siz *= 2
}
return siz
}
var IntSize = computeIntsize() const IntSize = intSize // number of bits in int, uint (32 or 64)
// Return the first number n such that n*base >= 1<<64. // Return the first number n such that n*base >= 1<<64.
func cutoff64(base int) uint64 { func cutoff64(base int) uint64 {
...@@ -38,17 +32,13 @@ func cutoff64(base int) uint64 { ...@@ -38,17 +32,13 @@ func cutoff64(base int) uint64 {
return (1<<64-1)/uint64(base) + 1 return (1<<64-1)/uint64(base) + 1
} }
// Btoui64 interprets a string s in an arbitrary base b (2 to 36) // ParseUint is like ParseInt but for unsigned numbers.
// and returns the corresponding value n. If b == 0, the base func ParseUint(s string, b int, bitSize int) (n uint64, err error) {
// is taken from the string prefix: base 16 for "0x", base 8 for "0", var cutoff, maxVal uint64
// and base 10 otherwise.
// if bitSize == 0 {
// The errors that Btoui64 returns have concrete type *NumError bitSize = int(IntSize)
// and include err.Num = s. If s is empty or contains invalid }
// digits, err.Error = ErrSyntax; if the value corresponding
// to s cannot be represented by a uint64, err.Error = ErrRange.
func Btoui64(s string, b int) (n uint64, err error) {
var cutoff uint64
s0 := s s0 := s
switch { switch {
...@@ -82,6 +72,7 @@ func Btoui64(s string, b int) (n uint64, err error) { ...@@ -82,6 +72,7 @@ func Btoui64(s string, b int) (n uint64, err error) {
n = 0 n = 0
cutoff = cutoff64(b) cutoff = cutoff64(b)
maxVal = 1<<uint(bitSize) - 1
for i := 0; i < len(s); i++ { for i := 0; i < len(s); i++ {
var v byte var v byte
...@@ -113,7 +104,7 @@ func Btoui64(s string, b int) (n uint64, err error) { ...@@ -113,7 +104,7 @@ func Btoui64(s string, b int) (n uint64, err error) {
n *= uint64(b) n *= uint64(b)
n1 := n + uint64(v) n1 := n + uint64(v)
if n1 < n { if n1 < n || n1 > maxVal {
// n+v overflows // n+v overflows
n = 1<<64 - 1 n = 1<<64 - 1
err = ErrRange err = ErrRange
...@@ -128,18 +119,25 @@ Error: ...@@ -128,18 +119,25 @@ Error:
return n, &NumError{s0, err} return n, &NumError{s0, err}
} }
// Atoui64 interprets a string s as a decimal number and // ParseInt interprets a string s in an arbitrary base b (2 to 36)
// returns the corresponding value n. // and returns the corresponding value n. If b == 0, the base
// is taken from the string prefix: base 16 for "0x", base 8 for "0",
// and base 10 otherwise.
// //
// Atoui64 returns err.Error = ErrSyntax if s is empty or contains invalid digits. // The bitSize argument specifies the integer type
// It returns err.Error = ErrRange if s cannot be represented by a uint64. // that the result must fit into. Bit sizes 0, 8, 16, 32, and 64
func Atoui64(s string) (n uint64, err error) { // correspond to int, int8, int16, int32, and int64.
return Btoui64(s, 10) //
} // The errors that ParseInt returns have concrete type *NumError
// and include err.Num = s. If s is empty or contains invalid
// digits, err.Error = ErrSyntax; if the value corresponding
// to s cannot be represented by a signed integer of the
// given size, err.Error = ErrRange.
func ParseInt(s string, base int, bitSize int) (i int64, err error) {
if bitSize == 0 {
bitSize = int(IntSize)
}
// Btoi64 is like Btoui64 but allows signed numbers and
// returns its result in an int64.
func Btoi64(s string, base int) (i int64, err error) {
// Empty string bad. // Empty string bad.
if len(s) == 0 { if len(s) == 0 {
return 0, &NumError{s, ErrSyntax} return 0, &NumError{s, ErrSyntax}
...@@ -157,16 +155,17 @@ func Btoi64(s string, base int) (i int64, err error) { ...@@ -157,16 +155,17 @@ func Btoi64(s string, base int) (i int64, err error) {
// Convert unsigned and check range. // Convert unsigned and check range.
var un uint64 var un uint64
un, err = Btoui64(s, base) un, err = ParseUint(s, base, bitSize)
if err != nil && err.(*NumError).Err != ErrRange { if err != nil && err.(*NumError).Err != ErrRange {
err.(*NumError).Num = s0 err.(*NumError).Num = s0
return 0, err return 0, err
} }
if !neg && un >= 1<<63 { cutoff := uint64(1 << uint(bitSize-1))
return 1<<63 - 1, &NumError{s0, ErrRange} if !neg && un >= cutoff {
return int64(cutoff - 1), &NumError{s0, ErrRange}
} }
if neg && un > 1<<63 { if neg && un > cutoff {
return -1 << 63, &NumError{s0, ErrRange} return -int64(cutoff), &NumError{s0, ErrRange}
} }
n := int64(un) n := int64(un)
if neg { if neg {
...@@ -175,35 +174,8 @@ func Btoi64(s string, base int) (i int64, err error) { ...@@ -175,35 +174,8 @@ func Btoi64(s string, base int) (i int64, err error) {
return n, nil return n, nil
} }
// Atoi64 is like Atoui64 but allows signed numbers and // Atoi is shorthand for ParseInt(s, 10, 0).
// returns its result in an int64.
func Atoi64(s string) (i int64, err error) { return Btoi64(s, 10) }
// Atoui is like Atoui64 but returns its result as a uint.
func Atoui(s string) (i uint, err error) {
i1, e1 := Atoui64(s)
if e1 != nil && e1.(*NumError).Err != ErrRange {
return 0, e1
}
i = uint(i1)
if uint64(i) != i1 {
return ^uint(0), &NumError{s, ErrRange}
}
return i, nil
}
// Atoi is like Atoi64 but returns its result as an int.
func Atoi(s string) (i int, err error) { func Atoi(s string) (i int, err error) {
i1, e1 := Atoi64(s) i64, err := ParseInt(s, 10, 0)
if e1 != nil && e1.(*NumError).Err != ErrRange { return int(i64), err
return 0, e1
}
i = int(i1)
if int64(i) != i1 {
if i1 < 0 {
return -1 << (IntSize - 1), &NumError{s, ErrRange}
}
return 1<<(IntSize-1) - 1, &NumError{s, ErrRange}
}
return i, nil
} }
...@@ -187,10 +187,10 @@ func init() { ...@@ -187,10 +187,10 @@ func init() {
} }
} }
func TestAtoui64(t *testing.T) { func TestParseUint64(t *testing.T) {
for i := range atoui64tests { for i := range atoui64tests {
test := &atoui64tests[i] test := &atoui64tests[i]
out, err := Atoui64(test.in) out, err := ParseUint(test.in, 10, 64)
if test.out != out || !reflect.DeepEqual(test.err, err) { if test.out != out || !reflect.DeepEqual(test.err, err) {
t.Errorf("Atoui64(%q) = %v, %v want %v, %v", t.Errorf("Atoui64(%q) = %v, %v want %v, %v",
test.in, out, err, test.out, test.err) test.in, out, err, test.out, test.err)
...@@ -198,21 +198,21 @@ func TestAtoui64(t *testing.T) { ...@@ -198,21 +198,21 @@ func TestAtoui64(t *testing.T) {
} }
} }
func TestBtoui64(t *testing.T) { func TestParseUint64Base(t *testing.T) {
for i := range btoui64tests { for i := range btoui64tests {
test := &btoui64tests[i] test := &btoui64tests[i]
out, err := Btoui64(test.in, 0) out, err := ParseUint(test.in, 0, 64)
if test.out != out || !reflect.DeepEqual(test.err, err) { if test.out != out || !reflect.DeepEqual(test.err, err) {
t.Errorf("Btoui64(%q) = %v, %v want %v, %v", t.Errorf("ParseUint(%q) = %v, %v want %v, %v",
test.in, out, err, test.out, test.err) test.in, out, err, test.out, test.err)
} }
} }
} }
func TestAtoi64(t *testing.T) { func TestParseInt64(t *testing.T) {
for i := range atoi64tests { for i := range atoi64tests {
test := &atoi64tests[i] test := &atoi64tests[i]
out, err := Atoi64(test.in) out, err := ParseInt(test.in, 10, 64)
if test.out != out || !reflect.DeepEqual(test.err, err) { if test.out != out || !reflect.DeepEqual(test.err, err) {
t.Errorf("Atoi64(%q) = %v, %v want %v, %v", t.Errorf("Atoi64(%q) = %v, %v want %v, %v",
test.in, out, err, test.out, test.err) test.in, out, err, test.out, test.err)
...@@ -220,23 +220,23 @@ func TestAtoi64(t *testing.T) { ...@@ -220,23 +220,23 @@ func TestAtoi64(t *testing.T) {
} }
} }
func TestBtoi64(t *testing.T) { func TestParseInt64Base(t *testing.T) {
for i := range btoi64tests { for i := range btoi64tests {
test := &btoi64tests[i] test := &btoi64tests[i]
out, err := Btoi64(test.in, 0) out, err := ParseInt(test.in, 0, 64)
if test.out != out || !reflect.DeepEqual(test.err, err) { if test.out != out || !reflect.DeepEqual(test.err, err) {
t.Errorf("Btoi64(%q) = %v, %v want %v, %v", t.Errorf("ParseInt(%q) = %v, %v want %v, %v",
test.in, out, err, test.out, test.err) test.in, out, err, test.out, test.err)
} }
} }
} }
func TestAtoui(t *testing.T) { func TestParseUint(t *testing.T) {
switch IntSize { switch IntSize {
case 32: case 32:
for i := range atoui32tests { for i := range atoui32tests {
test := &atoui32tests[i] test := &atoui32tests[i]
out, err := Atoui(test.in) out, err := ParseUint(test.in, 10, 0)
if test.out != uint32(out) || !reflect.DeepEqual(test.err, err) { if test.out != uint32(out) || !reflect.DeepEqual(test.err, err) {
t.Errorf("Atoui(%q) = %v, %v want %v, %v", t.Errorf("Atoui(%q) = %v, %v want %v, %v",
test.in, out, err, test.out, test.err) test.in, out, err, test.out, test.err)
...@@ -245,7 +245,7 @@ func TestAtoui(t *testing.T) { ...@@ -245,7 +245,7 @@ func TestAtoui(t *testing.T) {
case 64: case 64:
for i := range atoui64tests { for i := range atoui64tests {
test := &atoui64tests[i] test := &atoui64tests[i]
out, err := Atoui(test.in) out, err := ParseUint(test.in, 10, 0)
if test.out != uint64(out) || !reflect.DeepEqual(test.err, err) { if test.out != uint64(out) || !reflect.DeepEqual(test.err, err) {
t.Errorf("Atoui(%q) = %v, %v want %v, %v", t.Errorf("Atoui(%q) = %v, %v want %v, %v",
test.in, out, err, test.out, test.err) test.in, out, err, test.out, test.err)
...@@ -254,12 +254,12 @@ func TestAtoui(t *testing.T) { ...@@ -254,12 +254,12 @@ func TestAtoui(t *testing.T) {
} }
} }
func TestAtoi(t *testing.T) { func TestParseInt(t *testing.T) {
switch IntSize { switch IntSize {
case 32: case 32:
for i := range atoi32tests { for i := range atoi32tests {
test := &atoi32tests[i] test := &atoi32tests[i]
out, err := Atoi(test.in) out, err := ParseInt(test.in, 10, 0)
if test.out != int32(out) || !reflect.DeepEqual(test.err, err) { if test.out != int32(out) || !reflect.DeepEqual(test.err, err) {
t.Errorf("Atoi(%q) = %v, %v want %v, %v", t.Errorf("Atoi(%q) = %v, %v want %v, %v",
test.in, out, err, test.out, test.err) test.in, out, err, test.out, test.err)
...@@ -268,7 +268,7 @@ func TestAtoi(t *testing.T) { ...@@ -268,7 +268,7 @@ func TestAtoi(t *testing.T) {
case 64: case 64:
for i := range atoi64tests { for i := range atoi64tests {
test := &atoi64tests[i] test := &atoi64tests[i]
out, err := Atoi(test.in) out, err := ParseInt(test.in, 10, 0)
if test.out != int64(out) || !reflect.DeepEqual(test.err, err) { if test.out != int64(out) || !reflect.DeepEqual(test.err, err) {
t.Errorf("Atoi(%q) = %v, %v want %v, %v", t.Errorf("Atoi(%q) = %v, %v want %v, %v",
test.in, out, err, test.out, test.err) test.in, out, err, test.out, test.err)
...@@ -279,24 +279,24 @@ func TestAtoi(t *testing.T) { ...@@ -279,24 +279,24 @@ func TestAtoi(t *testing.T) {
func BenchmarkAtoi(b *testing.B) { func BenchmarkAtoi(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
Atoi("12345678") ParseInt("12345678", 10, 0)
} }
} }
func BenchmarkAtoiNeg(b *testing.B) { func BenchmarkAtoiNeg(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
Atoi("-12345678") ParseInt("-12345678", 10, 0)
} }
} }
func BenchmarkAtoi64(b *testing.B) { func BenchmarkAtoi64(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
Atoi64("12345678901234") ParseInt("12345678901234", 10, 64)
} }
} }
func BenchmarkAtoi64Neg(b *testing.B) { func BenchmarkAtoi64Neg(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
Atoi64("-12345678901234") ParseInt("-12345678901234", 10, 64)
} }
} }
...@@ -31,7 +31,7 @@ func pow2(i int) float64 { ...@@ -31,7 +31,7 @@ func pow2(i int) float64 {
func myatof64(s string) (f float64, ok bool) { func myatof64(s string) (f float64, ok bool) {
a := strings.SplitN(s, "p", 2) a := strings.SplitN(s, "p", 2)
if len(a) == 2 { if len(a) == 2 {
n, err := strconv.Atoi64(a[0]) n, err := strconv.ParseInt(a[0], 10, 64)
if err != nil { if err != nil {
return 0, false return 0, false
} }
...@@ -63,7 +63,7 @@ func myatof64(s string) (f float64, ok bool) { ...@@ -63,7 +63,7 @@ func myatof64(s string) (f float64, ok bool) {
} }
return v * pow2(e), true return v * pow2(e), true
} }
f1, err := strconv.Atof64(s) f1, err := strconv.ParseFloat(s, 64)
if err != nil { if err != nil {
return 0, false return 0, false
} }
...@@ -87,7 +87,8 @@ func myatof32(s string) (f float32, ok bool) { ...@@ -87,7 +87,8 @@ func myatof32(s string) (f float32, ok bool) {
} }
return float32(float64(n) * pow2(e)), true return float32(float64(n) * pow2(e)), true
} }
f1, err1 := strconv.Atof32(s) f64, err1 := strconv.ParseFloat(s, 32)
f1 := float32(f64)
if err1 != nil { if err1 != nil {
return 0, false return 0, false
} }
......
...@@ -22,8 +22,10 @@ type floatInfo struct { ...@@ -22,8 +22,10 @@ type floatInfo struct {
var float32info = floatInfo{23, 8, -127} var float32info = floatInfo{23, 8, -127}
var float64info = floatInfo{52, 11, -1023} var float64info = floatInfo{52, 11, -1023}
// Ftoa32 converts the 32-bit floating-point number f to a string, // FormatFloat converts the floating-point number f to a string,
// according to the format fmt and precision prec. // according to the format fmt and precision prec. It rounds the
// result assuming that the original was obtained from a floating-point
// value of bitSize bits (32 for float32, 64 for float64).
// //
// The format fmt is one of // The format fmt is one of
// 'b' (-ddddp±ddd, a binary exponent), // 'b' (-ddddp±ddd, a binary exponent),
...@@ -43,24 +45,17 @@ var float64info = floatInfo{52, 11, -1023} ...@@ -43,24 +45,17 @@ var float64info = floatInfo{52, 11, -1023}
// Ftoa32(f) is not the same as Ftoa64(float32(f)), // Ftoa32(f) is not the same as Ftoa64(float32(f)),
// because correct rounding and the number of digits // because correct rounding and the number of digits
// needed to identify f depend on the precision of the representation. // needed to identify f depend on the precision of the representation.
func Ftoa32(f float32, fmt byte, prec int) string { func FormatFloat(f float64, fmt byte, prec int, n int) string {
return genericFtoa(uint64(math.Float32bits(f)), fmt, prec, &float32info) if n == 32 {
} return genericFtoa(uint64(math.Float32bits(float32(f))), fmt, prec, &float32info)
}
// Ftoa64 is like Ftoa32 but converts a 64-bit floating-point number.
func Ftoa64(f float64, fmt byte, prec int) string {
return genericFtoa(math.Float64bits(f), fmt, prec, &float64info) return genericFtoa(math.Float64bits(f), fmt, prec, &float64info)
} }
// FtoaN converts the 64-bit floating-point number f to a string, // AppendFloat appends the string form of the floating-point number f,
// according to the format fmt and precision prec, but it rounds the // as generated by FormatFloat, to dst and returns the extended buffer.
// result assuming that it was obtained from a floating-point value func AppendFloat(dst []byte, f float64, fmt byte, prec int, n int) []byte {
// of n bits (32 or 64). return append(dst, FormatFloat(f, fmt, prec, n)...)
func FtoaN(f float64, fmt byte, prec int, n int) string {
if n == 32 {
return Ftoa32(float32(f), fmt, prec)
}
return Ftoa64(f, fmt, prec)
} }
func genericFtoa(bits uint64, fmt byte, prec int, flt *floatInfo) string { func genericFtoa(bits uint64, fmt byte, prec int, flt *floatInfo) string {
......
...@@ -128,47 +128,47 @@ var ftoatests = []ftoaTest{ ...@@ -128,47 +128,47 @@ var ftoatests = []ftoaTest{
func TestFtoa(t *testing.T) { func TestFtoa(t *testing.T) {
for i := 0; i < len(ftoatests); i++ { for i := 0; i < len(ftoatests); i++ {
test := &ftoatests[i] test := &ftoatests[i]
s := Ftoa64(test.f, test.fmt, test.prec) s := FormatFloat(test.f, test.fmt, test.prec, 64)
if s != test.s {
t.Error("test", test.f, string(test.fmt), test.prec, "want", test.s, "got", s)
}
s = FtoaN(test.f, test.fmt, test.prec, 64)
if s != test.s { if s != test.s {
t.Error("testN=64", test.f, string(test.fmt), test.prec, "want", test.s, "got", s) t.Error("testN=64", test.f, string(test.fmt), test.prec, "want", test.s, "got", s)
} }
x := AppendFloat([]byte("abc"), test.f, test.fmt, test.prec, 64)
if string(x) != "abc"+test.s {
t.Error("AppendFloat testN=64", test.f, string(test.fmt), test.prec, "want", "abc"+test.s, "got", string(x))
}
if float64(float32(test.f)) == test.f && test.fmt != 'b' { if float64(float32(test.f)) == test.f && test.fmt != 'b' {
s := Ftoa32(float32(test.f), test.fmt, test.prec) s := FormatFloat(test.f, test.fmt, test.prec, 32)
if s != test.s {
t.Error("test32", test.f, string(test.fmt), test.prec, "want", test.s, "got", s)
}
s = FtoaN(test.f, test.fmt, test.prec, 32)
if s != test.s { if s != test.s {
t.Error("testN=32", test.f, string(test.fmt), test.prec, "want", test.s, "got", s) t.Error("testN=32", test.f, string(test.fmt), test.prec, "want", test.s, "got", s)
} }
x := AppendFloat([]byte("abc"), test.f, test.fmt, test.prec, 32)
if string(x) != "abc"+test.s {
t.Error("AppendFloat testN=32", test.f, string(test.fmt), test.prec, "want", "abc"+test.s, "got", string(x))
}
} }
} }
} }
func BenchmarkFtoa64Decimal(b *testing.B) { func BenchmarkFtoa64Decimal(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
Ftoa64(33909, 'g', -1) FormatFloat(33909, 'g', -1, 64)
} }
} }
func BenchmarkFtoa64Float(b *testing.B) { func BenchmarkFtoa64Float(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
Ftoa64(339.7784, 'g', -1) FormatFloat(339.7784, 'g', -1, 64)
} }
} }
func BenchmarkFtoa64FloatExp(b *testing.B) { func BenchmarkFtoa64FloatExp(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
Ftoa64(-5.09e75, 'g', -1) FormatFloat(-5.09e75, 'g', -1, 64)
} }
} }
func BenchmarkFtoa64Big(b *testing.B) { func BenchmarkFtoa64Big(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
Ftoa64(123456789123456789123456789, 'g', -1) FormatFloat(123456789123456789123456789, 'g', -1, 64)
} }
} }
...@@ -4,10 +4,11 @@ ...@@ -4,10 +4,11 @@
package strconv package strconv
// Uitob64 returns the string representation of i in the given base. // FormatUint returns the string representation of i in the given base.
func Uitob64(u uint64, base uint) string { func FormatUint(i uint64, base int) string {
u := i
if base < 2 || 36 < base { if base < 2 || 36 < base {
panic("invalid base " + Uitoa(base)) panic("invalid base " + Itoa(base))
} }
if u == 0 { if u == 0 {
return "0" return "0"
...@@ -26,32 +27,31 @@ func Uitob64(u uint64, base uint) string { ...@@ -26,32 +27,31 @@ func Uitob64(u uint64, base uint) string {
return string(buf[j:]) return string(buf[j:])
} }
// Itob64 returns the string representation of i in the given base. // FormatInt returns the string representation of i in the given base.
func Itob64(i int64, base uint) string { func FormatInt(i int64, base int) string {
if i == 0 { if i == 0 {
return "0" return "0"
} }
if i < 0 { if i < 0 {
return "-" + Uitob64(-uint64(i), base) return "-" + FormatUint(-uint64(i), base)
} }
return Uitob64(uint64(i), base) return FormatUint(uint64(i), base)
} }
// Itoa64 returns the decimal string representation of i. // Itoa is shorthand for FormatInt(i, 10).
func Itoa64(i int64) string { return Itob64(i, 10) } func Itoa(i int) string {
return FormatInt(int64(i), 10)
// Uitoa64 returns the decimal string representation of i. }
func Uitoa64(i uint64) string { return Uitob64(i, 10) }
// Uitob returns the string representation of i in the given base.
func Uitob(i uint, base uint) string { return Uitob64(uint64(i), base) }
// Itob returns the string representation of i in the given base.
func Itob(i int, base uint) string { return Itob64(int64(i), base) }
// Itoa returns the decimal string representation of i. // AppendInt appends the string form of the integer i,
func Itoa(i int) string { return Itob64(int64(i), 10) } // as generated by FormatInt, to dst and returns the extended buffer.
func AppendInt(dst []byte, i int64, base int) []byte {
return append(dst, FormatInt(i, base)...)
}
// Uitoa returns the decimal string representation of i. // AppendUint appends the string form of the unsigned integer i,
func Uitoa(i uint) string { return Uitob64(uint64(i), 10) } // as generated by FormatUint, to dst and returns the extended buffer.
func AppendUint(dst []byte, i uint64, base int) []byte {
return append(dst, FormatUint(i, base)...)
}
...@@ -11,7 +11,7 @@ import ( ...@@ -11,7 +11,7 @@ import (
type itob64Test struct { type itob64Test struct {
in int64 in int64
base uint base int
out string out string
} }
...@@ -60,73 +60,43 @@ var itob64tests = []itob64Test{ ...@@ -60,73 +60,43 @@ var itob64tests = []itob64Test{
func TestItoa(t *testing.T) { func TestItoa(t *testing.T) {
for _, test := range itob64tests { for _, test := range itob64tests {
s := Itob64(test.in, test.base) s := FormatInt(test.in, test.base)
if s != test.out { if s != test.out {
t.Errorf("Itob64(%v, %v) = %v want %v", t.Errorf("FormatInt(%v, %v) = %v want %v",
test.in, test.base, s, test.out) test.in, test.base, s, test.out)
} }
x := AppendInt([]byte("abc"), test.in, test.base)
if string(x) != "abc"+test.out {
t.Errorf("AppendInt(%q, %v, %v) = %q want %v",
"abc", test.in, test.base, x, test.out)
}
if test.in >= 0 { if test.in >= 0 {
s := Uitob64(uint64(test.in), test.base) s := FormatUint(uint64(test.in), test.base)
if s != test.out { if s != test.out {
t.Errorf("Uitob64(%v, %v) = %v want %v", t.Errorf("FormatUint(%v, %v) = %v want %v",
test.in, test.base, s, test.out) test.in, test.base, s, test.out)
} }
} x := AppendUint([]byte("abc"), uint64(test.in), test.base)
if string(x) != "abc"+test.out {
if int64(int(test.in)) == test.in { t.Errorf("AppendUint(%q, %v, %v) = %q want %v",
s := Itob(int(test.in), test.base) "abc", uint64(test.in), test.base, x, test.out)
if s != test.out {
t.Errorf("Itob(%v, %v) = %v want %v",
test.in, test.base, s, test.out)
}
if test.in >= 0 {
s := Uitob(uint(test.in), test.base)
if s != test.out {
t.Errorf("Uitob(%v, %v) = %v want %v",
test.in, test.base, s, test.out)
}
} }
} }
if test.base == 10 { if test.base == 10 && int64(int(test.in)) == test.in {
s := Itoa64(test.in) s := Itoa(int(test.in))
if s != test.out { if s != test.out {
t.Errorf("Itoa64(%v) = %v want %v", t.Errorf("Itoa(%v) = %v want %v",
test.in, s, test.out) test.in, s, test.out)
} }
if test.in >= 0 {
s := Uitob64(uint64(test.in), test.base)
if s != test.out {
t.Errorf("Uitob64(%v, %v) = %v want %v",
test.in, test.base, s, test.out)
}
}
if int64(int(test.in)) == test.in {
s := Itoa(int(test.in))
if s != test.out {
t.Errorf("Itoa(%v) = %v want %v",
test.in, s, test.out)
}
if test.in >= 0 {
s := Uitoa(uint(test.in))
if s != test.out {
t.Errorf("Uitoa(%v) = %v want %v",
test.in, s, test.out)
}
}
}
} }
} }
} }
type uitob64Test struct { type uitob64Test struct {
in uint64 in uint64
base uint base int
out string out string
} }
...@@ -141,34 +111,16 @@ var uitob64tests = []uitob64Test{ ...@@ -141,34 +111,16 @@ var uitob64tests = []uitob64Test{
func TestUitoa(t *testing.T) { func TestUitoa(t *testing.T) {
for _, test := range uitob64tests { for _, test := range uitob64tests {
s := Uitob64(test.in, test.base) s := FormatUint(test.in, test.base)
if s != test.out { if s != test.out {
t.Errorf("Uitob64(%v, %v) = %v want %v", t.Errorf("FormatUint(%v, %v) = %v want %v",
test.in, test.base, s, test.out) test.in, test.base, s, test.out)
} }
x := AppendUint([]byte("abc"), test.in, test.base)
if uint64(uint(test.in)) == test.in { if string(x) != "abc"+test.out {
s := Uitob(uint(test.in), test.base) t.Errorf("AppendUint(%q, %v, %v) = %q want %v",
if s != test.out { "abc", test.in, test.base, x, test.out)
t.Errorf("Uitob(%v, %v) = %v want %v",
test.in, test.base, s, test.out)
}
} }
if test.base == 10 {
s := Uitoa64(test.in)
if s != test.out {
t.Errorf("Uitoa64(%v) = %v want %v",
test.in, s, test.out)
}
if uint64(uint(test.in)) == test.in {
s := Uitoa(uint(test.in))
if s != test.out {
t.Errorf("Uitoa(%v) = %v want %v",
test.in, s, test.out)
}
}
}
} }
} }
...@@ -92,6 +92,12 @@ func Quote(s string) string { ...@@ -92,6 +92,12 @@ func Quote(s string) string {
return quoteWith(s, '"', false) return quoteWith(s, '"', false)
} }
// AppendQuote appends a double-quoted Go string literal representing s,
// as generated by Quote, to dst and returns the extended buffer.
func AppendQuote(dst []byte, s string) []byte {
return append(dst, Quote(s)...)
}
// QuoteToASCII returns a double-quoted Go string literal representing s. // QuoteToASCII returns a double-quoted Go string literal representing s.
// The returned string uses Go escape sequences (\t, \n, \xFF, \u0100) for // The returned string uses Go escape sequences (\t, \n, \xFF, \u0100) for
// non-ASCII characters and non-printable characters as defined by // non-ASCII characters and non-printable characters as defined by
...@@ -100,6 +106,12 @@ func QuoteToASCII(s string) string { ...@@ -100,6 +106,12 @@ func QuoteToASCII(s string) string {
return quoteWith(s, '"', true) return quoteWith(s, '"', true)
} }
// AppendQuoteToASCII appends a double-quoted Go string literal representing s,
// as generated by QuoteToASCII, to dst and returns the extended buffer.
func AppendQuoteToASCII(dst []byte, s string) []byte {
return append(dst, QuoteToASCII(s)...)
}
// QuoteRune returns a single-quoted Go character literal representing the // QuoteRune returns a single-quoted Go character literal representing the
// rune. The returned string uses Go escape sequences (\t, \n, \xFF, \u0100) // rune. The returned string uses Go escape sequences (\t, \n, \xFF, \u0100)
// for control characters and non-printable characters as defined by // for control characters and non-printable characters as defined by
...@@ -109,6 +121,12 @@ func QuoteRune(rune int) string { ...@@ -109,6 +121,12 @@ func QuoteRune(rune int) string {
return quoteWith(string(rune), '\'', false) return quoteWith(string(rune), '\'', false)
} }
// AppendQuoteRune appends a single-quoted Go character literal representing the rune,
// as generated by QuoteRune, to dst and returns the extended buffer.
func AppendQuoteRune(dst []byte, rune int) []byte {
return append(dst, QuoteRune(rune)...)
}
// QuoteRuneToASCII returns a single-quoted Go character literal representing // QuoteRuneToASCII returns a single-quoted Go character literal representing
// the rune. The returned string uses Go escape sequences (\t, \n, \xFF, // the rune. The returned string uses Go escape sequences (\t, \n, \xFF,
// \u0100) for non-ASCII characters and non-printable characters as defined // \u0100) for non-ASCII characters and non-printable characters as defined
...@@ -118,6 +136,12 @@ func QuoteRuneToASCII(rune int) string { ...@@ -118,6 +136,12 @@ func QuoteRuneToASCII(rune int) string {
return quoteWith(string(rune), '\'', true) return quoteWith(string(rune), '\'', true)
} }
// AppendQuoteRune appends a single-quoted Go character literal representing the rune,
// as generated by QuoteRuneToASCII, to dst and returns the extended buffer.
func AppendQuoteRuneToASCII(dst []byte, rune int) []byte {
return append(dst, QuoteRuneToASCII(rune)...)
}
// CanBackquote returns whether the string s would be // CanBackquote returns whether the string s would be
// a valid Go string literal if enclosed in backquotes. // a valid Go string literal if enclosed in backquotes.
func CanBackquote(s string) bool { func CanBackquote(s string) bool {
......
...@@ -29,6 +29,9 @@ func TestQuote(t *testing.T) { ...@@ -29,6 +29,9 @@ func TestQuote(t *testing.T) {
if out := Quote(tt.in); out != tt.out { if out := Quote(tt.in); out != tt.out {
t.Errorf("Quote(%s) = %s, want %s", tt.in, out, tt.out) t.Errorf("Quote(%s) = %s, want %s", tt.in, out, tt.out)
} }
if out := AppendQuote([]byte("abc"), tt.in); string(out) != "abc"+tt.out {
t.Errorf("AppendQuote(%q, %s) = %s, want %s", "abc", tt.in, out, "abc"+tt.out)
}
} }
} }
...@@ -37,6 +40,9 @@ func TestQuoteToASCII(t *testing.T) { ...@@ -37,6 +40,9 @@ func TestQuoteToASCII(t *testing.T) {
if out := QuoteToASCII(tt.in); out != tt.ascii { if out := QuoteToASCII(tt.in); out != tt.ascii {
t.Errorf("QuoteToASCII(%s) = %s, want %s", tt.in, out, tt.ascii) t.Errorf("QuoteToASCII(%s) = %s, want %s", tt.in, out, tt.ascii)
} }
if out := AppendQuoteToASCII([]byte("abc"), tt.in); string(out) != "abc"+tt.ascii {
t.Errorf("AppendQuoteToASCII(%q, %s) = %s, want %s", "abc", tt.in, out, "abc"+tt.ascii)
}
} }
} }
...@@ -63,6 +69,9 @@ func TestQuoteRune(t *testing.T) { ...@@ -63,6 +69,9 @@ func TestQuoteRune(t *testing.T) {
if out := QuoteRune(tt.in); out != tt.out { if out := QuoteRune(tt.in); out != tt.out {
t.Errorf("QuoteRune(%U) = %s, want %s", tt.in, out, tt.out) t.Errorf("QuoteRune(%U) = %s, want %s", tt.in, out, tt.out)
} }
if out := AppendQuoteRune([]byte("abc"), tt.in); string(out) != "abc"+tt.out {
t.Errorf("AppendQuoteRune(%q, %U) = %s, want %s", "abc", tt.in, out, "abc"+tt.out)
}
} }
} }
...@@ -71,6 +80,9 @@ func TestQuoteRuneToASCII(t *testing.T) { ...@@ -71,6 +80,9 @@ func TestQuoteRuneToASCII(t *testing.T) {
if out := QuoteRuneToASCII(tt.in); out != tt.ascii { if out := QuoteRuneToASCII(tt.in); out != tt.ascii {
t.Errorf("QuoteRuneToASCII(%U) = %s, want %s", tt.in, out, tt.ascii) t.Errorf("QuoteRuneToASCII(%U) = %s, want %s", tt.in, out, tt.ascii)
} }
if out := AppendQuoteRuneToASCII([]byte("abc"), tt.in); string(out) != "abc"+tt.ascii {
t.Errorf("AppendQuoteRuneToASCII(%q, %U) = %s, want %s", "abc", tt.in, out, "abc"+tt.ascii)
}
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment