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:
parent
9ea0bd3986
commit
9fa6cb2f2b
@ -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)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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{
|
||||||
|
@ -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))
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user