1
0
mirror of https://github.com/golang/go synced 2024-11-24 21:00:09 -07:00

big: better support for string conversions

Fixes #1842.

R=r
CC=golang-dev
https://golang.org/cl/4517081
This commit is contained in:
Robert Griesemer 2011-05-17 15:32:38 -07:00
parent 9ea0bd3986
commit 9fa6cb2f2b
4 changed files with 58 additions and 37 deletions

View File

@ -313,28 +313,30 @@ func (x *Int) String() string {
if x.neg { if x.neg {
s = "-" s = "-"
} }
return s + x.abs.string(10) return s + x.abs.decimalString()
} }
func fmtbase(ch int) int { func charset(ch int) string {
switch ch { switch ch {
case 'b': case 'b':
return 2 return lowercaseDigits[0:2]
case 'o': case 'o':
return 8 return lowercaseDigits[0:8]
case 'd': case 'd':
return 10 return lowercaseDigits[0:10]
case 'x': case 'x':
return 16 return lowercaseDigits[0:16]
case 'X':
return uppercaseDigits[0:16]
} }
return 10 return "" // unknown format
} }
// Format is a support routine for fmt.Formatter. It accepts // Format is a support routine for fmt.Formatter. It accepts
// the formats 'b' (binary), 'o' (octal), 'd' (decimal) and // the formats 'b' (binary), 'o' (octal), 'd' (decimal), 'x'
// 'x' (hexadecimal). // (lowercase hexadecimal), and 'X' (uppercase hexadecimal).
// //
func (x *Int) Format(s fmt.State, ch int) { func (x *Int) Format(s fmt.State, ch int) {
if x == nil { if x == nil {
@ -344,7 +346,7 @@ func (x *Int) Format(s fmt.State, ch int) {
if x.neg { if x.neg {
fmt.Fprint(s, "-") fmt.Fprint(s, "-")
} }
fmt.Fprint(s, x.abs.string(fmtbase(ch))) fmt.Fprint(s, x.abs.string(charset(ch)))
} }

View File

@ -20,6 +20,7 @@ package big
import "rand" import "rand"
// An unsigned integer x of the form // An unsigned integer x of the form
// //
// x = x[n-1]*_B^(n-1) + x[n-2]*_B^(n-2) + ... + x[1]*_B + x[0] // x = x[n-1]*_B^(n-1) + x[n-2]*_B^(n-2) + ... + x[1]*_B + x[0]
@ -668,16 +669,23 @@ func (z nat) scan(s string, base int) (nat, int, int) {
} }
// string converts x to a string for a given base, with 2 <= base <= 16. // Character sets for string conversion.
// TODO(gri) in the style of the other routines, perhaps this should take const (
// a []byte buffer and return it lowercaseDigits = "0123456789abcdefghijklmnopqrstuvwxyz"
func (x nat) string(base int) string { uppercaseDigits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
if base < 2 || 16 < base { )
panic("illegal base")
}
if len(x) == 0 { // string converts x to a string using digits from a charset; a digit with
return "0" // value d is represented by charset[d]. The conversion base is determined
// by len(charset). If len(charset) < 2, the result is "<illegal base>".
func (x nat) string(charset string) string {
base := len(charset)
switch {
case base < 2:
return "<illegal base>"
case len(x) == 0:
return string(charset[0])
} }
// allocate buffer for conversion // allocate buffer for conversion
@ -692,13 +700,20 @@ func (x nat) string(base int) string {
i-- i--
var r Word var r Word
q, r = q.divW(q, Word(base)) q, r = q.divW(q, Word(base))
s[i] = "0123456789abcdef"[r] s[i] = charset[r]
} }
return string(s[i:]) return string(s[i:])
} }
// decimalString returns a decimal representation of x.
// It calls x.string with the charset "0123456789".
func (x nat) decimalString() string {
return x.string(lowercaseDigits[0:10])
}
const deBruijn32 = 0x077CB531 const deBruijn32 = 0x077CB531
var deBruijn32Lookup = []byte{ var deBruijn32Lookup = []byte{

View File

@ -133,7 +133,7 @@ var mulRangesN = []struct {
func TestMulRangeN(t *testing.T) { func TestMulRangeN(t *testing.T) {
for i, r := range mulRangesN { for i, r := range mulRangesN {
prod := nat(nil).mulRange(r.a, r.b).string(10) prod := nat(nil).mulRange(r.a, r.b).decimalString()
if prod != r.prod { if prod != r.prod {
t.Errorf("#%d: got %s; want %s", i, prod, r.prod) t.Errorf("#%d: got %s; want %s", i, prod, r.prod)
} }
@ -167,31 +167,35 @@ func BenchmarkMul(b *testing.B) {
} }
var tab = []struct { var strTests = []struct {
x nat x nat // nat value to be converted
b int c string // conversion charset
s string s string // expected result
}{ }{
{nil, 10, "0"}, {nil, "01", "0"},
{nat{1}, 10, "1"}, {nat{1}, "01", "1"},
{nat{10}, 10, "10"}, {nat{0xc5}, "01", "11000101"},
{nat{1234567890}, 10, "1234567890"}, {nat{03271}, lowercaseDigits[0:8], "3271"},
{nat{10}, lowercaseDigits[0:10], "10"},
{nat{1234567890}, uppercaseDigits[0:10], "1234567890"},
{nat{0xdeadbeef}, lowercaseDigits[0:16], "deadbeef"},
{nat{0xdeadbeef}, uppercaseDigits[0:16], "DEADBEEF"},
} }
func TestString(t *testing.T) { func TestString(t *testing.T) {
for _, a := range tab { for _, a := range strTests {
s := a.x.string(a.b) s := a.x.string(a.c)
if s != a.s { if s != a.s {
t.Errorf("string%+v\n\tgot s = %s; want %s", a, s, a.s) t.Errorf("string%+v\n\tgot s = %s; want %s", a, s, a.s)
} }
x, b, n := nat(nil).scan(a.s, a.b) x, b, n := nat(nil).scan(a.s, len(a.c))
if x.cmp(a.x) != 0 { if x.cmp(a.x) != 0 {
t.Errorf("scan%+v\n\tgot z = %v; want %v", a, x, a.x) t.Errorf("scan%+v\n\tgot z = %v; want %v", a, x, a.x)
} }
if b != a.b { if b != len(a.c) {
t.Errorf("scan%+v\n\tgot b = %d; want %d", a, b, a.b) t.Errorf("scan%+v\n\tgot b = %d; want %d", a, b, len(a.c))
} }
if n != len(a.s) { if n != len(a.s) {
t.Errorf("scan%+v\n\tgot n = %d; want %d", a, n, len(a.s)) t.Errorf("scan%+v\n\tgot n = %d; want %d", a, n, len(a.s))

View File

@ -270,7 +270,7 @@ func (z *Rat) SetString(s string) (*Rat, bool) {
// String returns a string representation of z in the form "a/b" (even if b == 1). // String returns a string representation of z in the form "a/b" (even if b == 1).
func (z *Rat) String() string { func (z *Rat) String() string {
return z.a.String() + "/" + z.b.string(10) return z.a.String() + "/" + z.b.decimalString()
} }
@ -311,13 +311,13 @@ func (z *Rat) FloatString(prec int) string {
} }
} }
s := q.string(10) s := q.decimalString()
if z.a.neg { if z.a.neg {
s = "-" + s s = "-" + s
} }
if prec > 0 { if prec > 0 {
rs := r.string(10) rs := r.decimalString()
leadingZeros := prec - len(rs) leadingZeros := prec - len(rs)
s += "." + strings.Repeat("0", leadingZeros) + rs s += "." + strings.Repeat("0", leadingZeros) + rs
} }