]> Cypherpunks repositories - gostls13.git/commitdiff
big: better support for string conversions
authorRobert Griesemer <gri@golang.org>
Tue, 17 May 2011 22:32:38 +0000 (15:32 -0700)
committerRobert Griesemer <gri@golang.org>
Tue, 17 May 2011 22:32:38 +0000 (15:32 -0700)
Fixes #1842.

R=r
CC=golang-dev
https://golang.org/cl/4517081

src/pkg/big/int.go
src/pkg/big/nat.go
src/pkg/big/nat_test.go
src/pkg/big/rat.go

index dfb7dcdb63fa86a0f55a05e6f4b66d2f2f17603e..2a3fe5faea63cf12123168b5fc3d239d090651e8 100755 (executable)
@@ -313,28 +313,30 @@ func (x *Int) String() string {
        if x.neg {
                s = "-"
        }
-       return s + x.abs.string(10)
+       return s + x.abs.decimalString()
 }
 
 
-func fmtbase(ch int) int {
+func charset(ch int) string {
        switch ch {
        case 'b':
-               return 2
+               return lowercaseDigits[0:2]
        case 'o':
-               return 8
+               return lowercaseDigits[0:8]
        case 'd':
-               return 10
+               return lowercaseDigits[0:10]
        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
-// the formats 'b' (binary), 'o' (octal), 'd' (decimal) and
-// 'x' (hexadecimal).
+// the formats 'b' (binary), 'o' (octal), 'd' (decimal), 'x'
+// (lowercase hexadecimal), and 'X' (uppercase hexadecimal).
 //
 func (x *Int) Format(s fmt.State, ch int) {
        if x == nil {
@@ -344,7 +346,7 @@ func (x *Int) Format(s fmt.State, ch int) {
        if x.neg {
                fmt.Fprint(s, "-")
        }
-       fmt.Fprint(s, x.abs.string(fmtbase(ch)))
+       fmt.Fprint(s, x.abs.string(charset(ch)))
 }
 
 
index 2fdae9f06f62d6178dcd212e967063457454b1b6..a5d8f223ab1effa4bf9ba57abd7078438e3a630f 100755 (executable)
@@ -20,6 +20,7 @@ package big
 
 import "rand"
 
+
 // 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]
@@ -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.
-// TODO(gri) in the style of the other routines, perhaps this should take
-//           a []byte buffer and return it
-func (x nat) string(base int) string {
-       if base < 2 || 16 < base {
-               panic("illegal base")
-       }
+// Character sets for string conversion.
+const (
+       lowercaseDigits = "0123456789abcdefghijklmnopqrstuvwxyz"
+       uppercaseDigits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+)
+
+// string converts x to a string using digits from a charset; a digit with
+// 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)
 
-       if len(x) == 0 {
-               return "0"
+       switch {
+       case base < 2:
+               return "<illegal base>"
+       case len(x) == 0:
+               return string(charset[0])
        }
 
        // allocate buffer for conversion
@@ -692,13 +700,20 @@ func (x nat) string(base int) string {
                i--
                var r Word
                q, r = q.divW(q, Word(base))
-               s[i] = "0123456789abcdef"[r]
+               s[i] = charset[r]
        }
 
        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
 
 var deBruijn32Lookup = []byte{
index 0bcb945548aa003b11f80b2138c348248731438f..a29843a3fa25224d34fcb0d94574fdec6f2a4449 100755 (executable)
@@ -133,7 +133,7 @@ var mulRangesN = []struct {
 
 func TestMulRangeN(t *testing.T) {
        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 {
                        t.Errorf("#%d: got %s; want %s", i, prod, r.prod)
                }
@@ -167,31 +167,35 @@ func BenchmarkMul(b *testing.B) {
 }
 
 
-var tab = []struct {
-       x nat
-       b int
-       s string
+var strTests = []struct {
+       x nat    // nat value to be converted
+       c string // conversion charset
+       s string // expected result
 }{
-       {nil, 10, "0"},
-       {nat{1}, 10, "1"},
-       {nat{10}, 10, "10"},
-       {nat{1234567890}, 10, "1234567890"},
+       {nil, "01", "0"},
+       {nat{1}, "01", "1"},
+       {nat{0xc5}, "01", "11000101"},
+       {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) {
-       for _, a := range tab {
-               s := a.x.string(a.b)
+       for _, a := range strTests {
+               s := a.x.string(a.c)
                if 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 {
                        t.Errorf("scan%+v\n\tgot z = %v; want %v", a, x, a.x)
                }
-               if b != a.b {
-                       t.Errorf("scan%+v\n\tgot b = %d; want %d", a, b, a.b)
+               if b != len(a.c) {
+                       t.Errorf("scan%+v\n\tgot b = %d; want %d", a, b, len(a.c))
                }
                if n != len(a.s) {
                        t.Errorf("scan%+v\n\tgot n = %d; want %d", a, n, len(a.s))
index e70673a1cbacd10381f247b6419adcbd28f7bf39..6b60be7e5db05968683e74d363864ccde584fb33 100644 (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).
 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 {
                s = "-" + s
        }
 
        if prec > 0 {
-               rs := r.string(10)
+               rs := r.decimalString()
                leadingZeros := prec - len(rs)
                s += "." + strings.Repeat("0", leadingZeros) + rs
        }