Commit b07a9efa authored by Robert Griesemer's avatar Robert Griesemer

math/big: faster string conversion routines

Eliminated unnecessary string conversions throughout and removed
(internal) capability for arbitrary character sets in conversion
routines (functionality was not exported and not used internally).

benchmark                         old ns/op     new ns/op     delta
BenchmarkDecimalConversion-8      198283        187085        -5.65%
BenchmarkStringPiParallel-8       46116         47822         +3.70%
BenchmarkString10Base2-8          216           166           -23.15%
BenchmarkString100Base2-8         886           762           -14.00%
BenchmarkString1000Base2-8        7296          6625          -9.20%
BenchmarkString10000Base2-8       72371         65563         -9.41%
BenchmarkString100000Base2-8      725849        672766        -7.31%
BenchmarkString10Base8-8          160           114           -28.75%
BenchmarkString100Base8-8         398           309           -22.36%
BenchmarkString1000Base8-8        2650          2244          -15.32%
BenchmarkString10000Base8-8       24974         21745         -12.93%
BenchmarkString100000Base8-8      245457        217489        -11.39%
BenchmarkString10Base10-8         337           288           -14.54%
BenchmarkString100Base10-8        1298          1046          -19.41%
BenchmarkString1000Base10-8       6200          5752          -7.23%
BenchmarkString10000Base10-8      24942         22589         -9.43%
BenchmarkString100000Base10-8     8012921       7947152       -0.82%
BenchmarkString10Base16-8         156           107           -31.41%
BenchmarkString100Base16-8        344           255           -25.87%
BenchmarkString1000Base16-8       2067          1705          -17.51%
BenchmarkString10000Base16-8      19026         16112         -15.32%
BenchmarkString100000Base16-8     184038        163457        -11.18%

Change-Id: I68bd807529bd9b985f4b6ac2a87764bcc1a7d2f7
Reviewed-on: https://go-review.googlesource.com/14926Reviewed-by: default avatarAlan Donovan <adonovan@google.com>
parent 007fa019
...@@ -80,7 +80,7 @@ func (x *decimal) init(m nat, shift int) { ...@@ -80,7 +80,7 @@ func (x *decimal) init(m nat, shift int) {
} }
// Convert mantissa into decimal representation. // Convert mantissa into decimal representation.
s := m.decimalString() // TODO(gri) avoid string conversion here s := m.itoa(10)
n := len(s) n := len(s)
x.exp = n x.exp = n
// Trim trailing zeros; instead the exponent is tracking // Trim trailing zeros; instead the exponent is tracking
......
...@@ -9,9 +9,9 @@ ...@@ -9,9 +9,9 @@
package big package big
import ( import (
"bytes"
"fmt" "fmt"
"strconv" "strconv"
"strings"
) )
// Text converts the floating-point number x to a string according // Text converts the floating-point number x to a string according
...@@ -323,7 +323,7 @@ func (x *Float) fmtB(buf []byte) []byte { ...@@ -323,7 +323,7 @@ func (x *Float) fmtB(buf []byte) []byte {
m = nat(nil).shr(m, uint(w-x.prec)) m = nat(nil).shr(m, uint(w-x.prec))
} }
buf = append(buf, m.decimalString()...) buf = append(buf, m.itoa(10)...)
buf = append(buf, 'p') buf = append(buf, 'p')
e := int64(x.exp) - int64(x.prec) e := int64(x.exp) - int64(x.prec)
if e >= 0 { if e >= 0 {
...@@ -357,7 +357,7 @@ func (x *Float) fmtP(buf []byte) []byte { ...@@ -357,7 +357,7 @@ func (x *Float) fmtP(buf []byte) []byte {
m = m[i:] m = m[i:]
buf = append(buf, "0x."...) buf = append(buf, "0x."...)
buf = append(buf, strings.TrimRight(m.hexString(), "0")...) buf = append(buf, bytes.TrimRight(m.itoa(16), "0")...)
buf = append(buf, 'p') buf = append(buf, 'p')
if x.exp >= 0 { if x.exp >= 0 {
buf = append(buf, '+') buf = append(buf, '+')
......
...@@ -17,25 +17,9 @@ func (x *Int) String() string { ...@@ -17,25 +17,9 @@ func (x *Int) String() string {
case x == nil: case x == nil:
return "<nil>" return "<nil>"
case x.neg: case x.neg:
return "-" + x.abs.decimalString() return "-" + string(x.abs.itoa(10))
} }
return x.abs.decimalString() return string(x.abs.itoa(10))
}
func charset(ch rune) string {
switch ch {
case 'b':
return lowercaseDigits[0:2]
case 'o':
return lowercaseDigits[0:8]
case 'd', 's', 'v':
return lowercaseDigits[0:10]
case 'x':
return lowercaseDigits[0:16]
case 'X':
return uppercaseDigits[0:16]
}
return "" // unknown format
} }
// write count copies of text to s // write count copies of text to s
...@@ -60,15 +44,24 @@ func writeMultiple(s fmt.State, text string, count int) { ...@@ -60,15 +44,24 @@ func writeMultiple(s fmt.State, text string, count int) {
// right justification. // right justification.
// //
func (x *Int) Format(s fmt.State, ch rune) { func (x *Int) Format(s fmt.State, ch rune) {
cs := charset(ch) // determine base
var base int
// special cases switch ch {
switch { case 'b':
case cs == "": base = 2
case 'o':
base = 8
case 'd', 's', 'v':
base = 10
case 'x', 'X':
base = 16
default:
// unknown format // unknown format
fmt.Fprintf(s, "%%!%c(big.Int=%s)", ch, x.String()) fmt.Fprintf(s, "%%!%c(big.Int=%s)", ch, x.String())
return return
case x == nil: }
if x == nil {
fmt.Fprint(s, "<nil>") fmt.Fprint(s, "<nil>")
return return
} }
...@@ -97,8 +90,15 @@ func (x *Int) Format(s fmt.State, ch rune) { ...@@ -97,8 +90,15 @@ func (x *Int) Format(s fmt.State, ch rune) {
} }
} }
// determine digits with base set by len(cs) and digit characters from cs digits := x.abs.itoa(base)
digits := x.abs.string(cs) if ch == 'X' {
// faster than bytes.ToUpper
for i, d := range digits {
if 'a' <= d && d <= 'z' {
digits[i] = 'A' + (d - 'a')
}
}
}
// number of characters for the three classes of number padding // number of characters for the three classes of number padding
var left int // space characters to left of digits for right justification ("%8d") var left int // space characters to left of digits for right justification ("%8d")
...@@ -111,7 +111,7 @@ func (x *Int) Format(s fmt.State, ch rune) { ...@@ -111,7 +111,7 @@ func (x *Int) Format(s fmt.State, ch rune) {
switch { switch {
case len(digits) < precision: case len(digits) < precision:
zeros = precision - len(digits) // count of zero padding zeros = precision - len(digits) // count of zero padding
case digits == "0" && precision == 0: case len(digits) == 1 && digits[0] == '0' && precision == 0:
return // print nothing if zero value (x == 0) and zero precision ("." or ".0") return // print nothing if zero value (x == 0) and zero precision ("." or ".0")
} }
} }
...@@ -137,7 +137,7 @@ func (x *Int) Format(s fmt.State, ch rune) { ...@@ -137,7 +137,7 @@ func (x *Int) Format(s fmt.State, ch rune) {
writeMultiple(s, sign, 1) writeMultiple(s, sign, 1)
writeMultiple(s, prefix, 1) writeMultiple(s, prefix, 1)
writeMultiple(s, "0", zeros) writeMultiple(s, "0", zeros)
writeMultiple(s, digits, 1) s.Write(digits)
writeMultiple(s, " ", right) writeMultiple(s, " ", right)
} }
......
...@@ -158,7 +158,7 @@ var mulRangesN = []struct { ...@@ -158,7 +158,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).decimalString() prod := string(nat(nil).mulRange(r.a, r.b).itoa(10))
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)
} }
...@@ -326,7 +326,7 @@ func TestTrailingZeroBits(t *testing.T) { ...@@ -326,7 +326,7 @@ func TestTrailingZeroBits(t *testing.T) {
for i := uint(0); i <= 3*_W; i++ { for i := uint(0); i <= 3*_W; i++ {
n := y.trailingZeroBits() n := y.trailingZeroBits()
if n != i { if n != i {
t.Errorf("got 0x%s.trailingZeroBits() = %d; want %d", y.hexString(), n, i) t.Errorf("got 0x%s.trailingZeroBits() = %d; want %d", y.itoa(16), n, i)
} }
y = y.shl(y, 1) y = y.shl(y, 1)
} }
...@@ -388,7 +388,7 @@ func TestMontgomery(t *testing.T) { ...@@ -388,7 +388,7 @@ func TestMontgomery(t *testing.T) {
z := nat(nil).montgomery(x, y, m, k0, len(m)) z := nat(nil).montgomery(x, y, m, k0, len(m))
z = z.norm() z = z.norm()
if z.cmp(out) != 0 { if z.cmp(out) != 0 {
t.Errorf("#%d got %s want %s", i, z.decimalString(), out.decimalString()) t.Errorf("#%d got %s want %s", i, z.itoa(10), out.itoa(10))
} }
} }
} }
...@@ -429,7 +429,7 @@ func TestExpNN(t *testing.T) { ...@@ -429,7 +429,7 @@ func TestExpNN(t *testing.T) {
z := nat(nil).expNN(x, y, m) z := nat(nil).expNN(x, y, m)
if z.cmp(out) != 0 { if z.cmp(out) != 0 {
t.Errorf("#%d got %s want %s", i, z.decimalString(), out.decimalString()) t.Errorf("#%d got %s want %s", i, z.itoa(10), out.itoa(10))
} }
} }
} }
...@@ -486,7 +486,7 @@ var fiboNums = []string{ ...@@ -486,7 +486,7 @@ var fiboNums = []string{
func TestFibo(t *testing.T) { func TestFibo(t *testing.T) {
for i, want := range fiboNums { for i, want := range fiboNums {
n := i * 10 n := i * 10
got := fibo(n).decimalString() got := string(fibo(n).itoa(10))
if got != want { if got != want {
t.Errorf("fibo(%d) failed: got %s want %s", n, got, want) t.Errorf("fibo(%d) failed: got %s want %s", n, got, want)
} }
......
...@@ -14,6 +14,11 @@ import ( ...@@ -14,6 +14,11 @@ import (
"sync" "sync"
) )
const digits = "0123456789abcdefghijklmnopqrstuvwxyz"
// Note: MaxBase = len(digits), but it must remain a rune constant
// for API compatibility.
// MaxBase is the largest number base accepted for string conversions. // MaxBase is the largest number base accepted for string conversions.
const MaxBase = 'z' - 'a' + 10 + 1 const MaxBase = 'z' - 'a' + 10 + 1
...@@ -229,45 +234,25 @@ func (z nat) scan(r io.ByteScanner, base int, fracOk bool) (res nat, b, count in ...@@ -229,45 +234,25 @@ func (z nat) scan(r io.ByteScanner, base int, fracOk bool) (res nat, b, count in
return return
} }
// Character sets for string conversion. // itoa converts x to an ASCII representation in the given base;
const ( // base must be between 2 and MaxBase, inclusive.
lowercaseDigits = "0123456789abcdefghijklmnopqrstuvwxyz" func (x nat) itoa(base int) []byte {
uppercaseDigits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" if base < 2 || base > MaxBase {
) panic("invalid base")
// decimalString returns a decimal representation of x.
// It calls x.string with the charset "0123456789".
func (x nat) decimalString() string {
return x.string(lowercaseDigits[:10])
}
// hexString returns a hexadecimal representation of x.
// It calls x.string with the charset "0123456789abcdef".
func (x nat) hexString() string {
return x.string(lowercaseDigits[:16])
}
// 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), which must be >= 2 and <= 256.
func (x nat) string(charset string) string {
b := Word(len(charset))
if b < 2 || b > 256 {
panic("invalid character set length")
} }
// x == 0 // x == 0
if len(x) == 0 { if len(x) == 0 {
return string(charset[0]) return []byte("0")
} }
// len(x) > 0 // len(x) > 0
// allocate buffer for conversion // allocate buffer for conversion
i := int(float64(x.bitLen())/math.Log2(float64(b))) + 1 // off by one at most i := int(float64(x.bitLen())/math.Log2(float64(base))) + 1 // off by 1 at most
s := make([]byte, i) s := make([]byte, i)
// convert power of two and non power of two bases separately // convert power of two and non power of two bases separately
if b == b&-b { if b := Word(base); b == b&-b {
// shift is base b digit size in bits // shift is base b digit size in bits
shift := trailingZeroBits(b) // shift > 0 because b >= 2 shift := trailingZeroBits(b) // shift > 0 because b >= 2
mask := Word(1<<shift - 1) mask := Word(1<<shift - 1)
...@@ -279,7 +264,7 @@ func (x nat) string(charset string) string { ...@@ -279,7 +264,7 @@ func (x nat) string(charset string) string {
// convert full digits // convert full digits
for nbits >= shift { for nbits >= shift {
i-- i--
s[i] = charset[w&mask] s[i] = digits[w&mask]
w >>= shift w >>= shift
nbits -= shift nbits -= shift
} }
...@@ -293,7 +278,7 @@ func (x nat) string(charset string) string { ...@@ -293,7 +278,7 @@ func (x nat) string(charset string) string {
// partial digit in current word w (== x[k-1]) and next word x[k] // partial digit in current word w (== x[k-1]) and next word x[k]
w |= x[k] << nbits w |= x[k] << nbits
i-- i--
s[i] = charset[w&mask] s[i] = digits[w&mask]
// advance // advance
w = x[k] >> (shift - nbits) w = x[k] >> (shift - nbits)
...@@ -304,7 +289,7 @@ func (x nat) string(charset string) string { ...@@ -304,7 +289,7 @@ func (x nat) string(charset string) string {
// convert digits of most-significant word w (omit leading zeros) // convert digits of most-significant word w (omit leading zeros)
for w != 0 { for w != 0 {
i-- i--
s[i] = charset[w&mask] s[i] = digits[w&mask]
w >>= shift w >>= shift
} }
...@@ -319,18 +304,18 @@ func (x nat) string(charset string) string { ...@@ -319,18 +304,18 @@ func (x nat) string(charset string) string {
q := nat(nil).set(x) q := nat(nil).set(x)
// convert q to string s in base b // convert q to string s in base b
q.convertWords(s, charset, b, ndigits, bb, table) q.convertWords(s, b, ndigits, bb, table)
// strip leading zeros // strip leading zeros
// (x != 0; thus s must contain at least one non-zero digit // (x != 0; thus s must contain at least one non-zero digit
// and the loop will terminate) // and the loop will terminate)
i = 0 i = 0
for zero := charset[0]; s[i] == zero; { for s[i] == '0' {
i++ i++
} }
} }
return string(s[i:]) return s[i:]
} }
// Convert words of q to base b digits in s. If q is large, it is recursively "split in half" // Convert words of q to base b digits in s. If q is large, it is recursively "split in half"
...@@ -349,7 +334,7 @@ func (x nat) string(charset string) string { ...@@ -349,7 +334,7 @@ func (x nat) string(charset string) string {
// ~30x for 20000 digits. Use nat_test.go's BenchmarkLeafSize tests to optimize leafSize for // ~30x for 20000 digits. Use nat_test.go's BenchmarkLeafSize tests to optimize leafSize for
// specific hardware. // specific hardware.
// //
func (q nat) convertWords(s []byte, charset string, b Word, ndigits int, bb Word, table []divisor) { func (q nat) convertWords(s []byte, b Word, ndigits int, bb Word, table []divisor) {
// split larger blocks recursively // split larger blocks recursively
if table != nil { if table != nil {
// len(q) > leafSize > 0 // len(q) > leafSize > 0
...@@ -374,8 +359,8 @@ func (q nat) convertWords(s []byte, charset string, b Word, ndigits int, bb Word ...@@ -374,8 +359,8 @@ func (q nat) convertWords(s []byte, charset string, b Word, ndigits int, bb Word
// convert subblocks and collect results in s[:h] and s[h:] // convert subblocks and collect results in s[:h] and s[h:]
h := len(s) - table[index].ndigits h := len(s) - table[index].ndigits
r.convertWords(s[h:], charset, b, ndigits, bb, table[0:index]) r.convertWords(s[h:], b, ndigits, bb, table[0:index])
s = s[:h] // == q.convertWords(s, charset, b, ndigits, bb, table[0:index+1]) s = s[:h] // == q.convertWords(s, b, ndigits, bb, table[0:index+1])
} }
} }
...@@ -393,7 +378,7 @@ func (q nat) convertWords(s []byte, charset string, b Word, ndigits int, bb Word ...@@ -393,7 +378,7 @@ func (q nat) convertWords(s []byte, charset string, b Word, ndigits int, bb Word
// this appears to be faster for BenchmarkString10000Base10 // this appears to be faster for BenchmarkString10000Base10
// and smaller strings (but a bit slower for larger ones) // and smaller strings (but a bit slower for larger ones)
t := r / 10 t := r / 10
s[i] = charset[r-t<<3-t-t] // TODO(gri) replace w/ t*10 once compiler produces better code s[i] = '0' + byte(r-t<<3-t-t) // TODO(gri) replace w/ t*10 once compiler produces better code
r = t r = t
} }
} }
...@@ -403,17 +388,16 @@ func (q nat) convertWords(s []byte, charset string, b Word, ndigits int, bb Word ...@@ -403,17 +388,16 @@ func (q nat) convertWords(s []byte, charset string, b Word, ndigits int, bb Word
q, r = q.divW(q, bb) q, r = q.divW(q, bb)
for j := 0; j < ndigits && i > 0; j++ { for j := 0; j < ndigits && i > 0; j++ {
i-- i--
s[i] = charset[r%b] s[i] = digits[r%b]
r /= b r /= b
} }
} }
} }
// prepend high-order zeros // prepend high-order zeros
zero := charset[0]
for i > 0 { // while need more leading zeros for i > 0 { // while need more leading zeros
i-- i--
s[i] = zero s[i] = '0'
} }
} }
......
...@@ -5,20 +5,19 @@ ...@@ -5,20 +5,19 @@
package big package big
import ( import (
"bytes"
"io" "io"
"strings" "strings"
"testing" "testing"
) )
func toString(x nat, charset string) string { func itoa(x nat, base int) []byte {
base := len(charset)
// special cases // special cases
switch { switch {
case base < 2: case base < 2:
panic("illegal base") panic("illegal base")
case len(x) == 0: case len(x) == 0:
return string(charset[0]) return []byte("0")
} }
// allocate buffer for conversion // allocate buffer for conversion
...@@ -33,54 +32,53 @@ func toString(x nat, charset string) string { ...@@ -33,54 +32,53 @@ func toString(x nat, charset string) 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] = charset[r] s[i] = digits[r]
} }
return string(s[i:]) return s[i:]
} }
var strTests = []struct { var strTests = []struct {
x nat // nat value to be converted x nat // nat value to be converted
c string // conversion charset b int // conversion base
s string // expected result s string // expected result
}{ }{
{nil, "01", "0"}, {nil, 2, "0"},
{nat{1}, "01", "1"}, {nat{1}, 2, "1"},
{nat{0xc5}, "01", "11000101"}, {nat{0xc5}, 2, "11000101"},
{nat{03271}, lowercaseDigits[:8], "3271"}, {nat{03271}, 8, "3271"},
{nat{10}, lowercaseDigits[:10], "10"}, {nat{10}, 10, "10"},
{nat{1234567890}, uppercaseDigits[:10], "1234567890"}, {nat{1234567890}, 10, "1234567890"},
{nat{0xdeadbeef}, lowercaseDigits[:16], "deadbeef"}, {nat{0xdeadbeef}, 16, "deadbeef"},
{nat{0xdeadbeef}, uppercaseDigits[:16], "DEADBEEF"}, {nat{0x229be7}, 17, "1a2b3c"},
{nat{0x229be7}, lowercaseDigits[:17], "1a2b3c"}, {nat{0x309663e6}, 32, "o9cov6"},
{nat{0x309663e6}, uppercaseDigits[:32], "O9COV6"},
} }
func TestString(t *testing.T) { func TestString(t *testing.T) {
// test invalid character set explicitly // test invalid base explicitly
var panicStr string var panicStr string
func() { func() {
defer func() { defer func() {
panicStr = recover().(string) panicStr = recover().(string)
}() }()
natOne.string("0") natOne.itoa(1)
}() }()
if panicStr != "invalid character set length" { if panicStr != "invalid base" {
t.Errorf("expected panic for invalid character set") t.Errorf("expected panic for invalid base")
} }
for _, a := range strTests { for _, a := range strTests {
s := a.x.string(a.c) s := string(a.x.itoa(a.b))
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, _, err := nat(nil).scan(strings.NewReader(a.s), len(a.c), false) x, b, _, err := nat(nil).scan(strings.NewReader(a.s), a.b, false)
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 != len(a.c) { if b != a.b {
t.Errorf("scan%+v\n\tgot b = %d; want %d", a, b, len(a.c)) t.Errorf("scan%+v\n\tgot b = %d; want %d", a, b, a.b)
} }
if err != nil { if err != nil {
t.Errorf("scan%+v\n\tgot error = %s", a, err) t.Errorf("scan%+v\n\tgot error = %s", a, err)
...@@ -236,7 +234,7 @@ func TestScanPi(t *testing.T) { ...@@ -236,7 +234,7 @@ func TestScanPi(t *testing.T) {
if err != nil { if err != nil {
t.Errorf("scanning pi: %s", err) t.Errorf("scanning pi: %s", err)
} }
if s := z.decimalString(); s != pi { if s := string(z.itoa(10)); s != pi {
t.Errorf("scanning pi: got %s", s) t.Errorf("scanning pi: got %s", s)
} }
} }
...@@ -265,12 +263,12 @@ func BenchmarkScanPi(b *testing.B) { ...@@ -265,12 +263,12 @@ func BenchmarkScanPi(b *testing.B) {
func BenchmarkStringPiParallel(b *testing.B) { func BenchmarkStringPiParallel(b *testing.B) {
var x nat var x nat
x, _, _, _ = x.scan(strings.NewReader(pi), 0, false) x, _, _, _ = x.scan(strings.NewReader(pi), 0, false)
if x.decimalString() != pi { if string(x.itoa(10)) != pi {
panic("benchmark incorrect: conversion failed") panic("benchmark incorrect: conversion failed")
} }
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
x.decimalString() x.itoa(10)
} }
}) })
} }
...@@ -304,15 +302,14 @@ func ScanHelper(b *testing.B, base int, x, y Word) { ...@@ -304,15 +302,14 @@ func ScanHelper(b *testing.B, base int, x, y Word) {
var z nat var z nat
z = z.expWW(x, y) z = z.expWW(x, y)
var s string s := z.itoa(base)
s = z.string(lowercaseDigits[:base]) if t := itoa(z, base); !bytes.Equal(s, t) {
if t := toString(z, lowercaseDigits[:base]); t != s {
b.Fatalf("scanning: got %s; want %s", s, t) b.Fatalf("scanning: got %s; want %s", s, t)
} }
b.StartTimer() b.StartTimer()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
z.scan(strings.NewReader(s), base, false) z.scan(bytes.NewReader(s), base, false)
} }
} }
...@@ -344,11 +341,11 @@ func StringHelper(b *testing.B, base int, x, y Word) { ...@@ -344,11 +341,11 @@ func StringHelper(b *testing.B, base int, x, y Word) {
b.StopTimer() b.StopTimer()
var z nat var z nat
z = z.expWW(x, y) z = z.expWW(x, y)
z.string(lowercaseDigits[:base]) // warm divisor cache z.itoa(base) // warm divisor cache
b.StartTimer() b.StartTimer()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
_ = z.string(lowercaseDigits[:base]) _ = z.itoa(base)
} }
} }
...@@ -372,7 +369,7 @@ func BenchmarkLeafSize16(b *testing.B) { LeafSizeHelper(b, 10, 16) } ...@@ -372,7 +369,7 @@ func BenchmarkLeafSize16(b *testing.B) { LeafSizeHelper(b, 10, 16) }
func BenchmarkLeafSize32(b *testing.B) { LeafSizeHelper(b, 10, 32) } // try some large lengths func BenchmarkLeafSize32(b *testing.B) { LeafSizeHelper(b, 10, 32) } // try some large lengths
func BenchmarkLeafSize64(b *testing.B) { LeafSizeHelper(b, 10, 64) } func BenchmarkLeafSize64(b *testing.B) { LeafSizeHelper(b, 10, 64) }
func LeafSizeHelper(b *testing.B, base Word, size int) { func LeafSizeHelper(b *testing.B, base, size int) {
b.StopTimer() b.StopTimer()
originalLeafSize := leafSize originalLeafSize := leafSize
resetTable(cacheBase10.table[:]) resetTable(cacheBase10.table[:])
...@@ -382,12 +379,12 @@ func LeafSizeHelper(b *testing.B, base Word, size int) { ...@@ -382,12 +379,12 @@ func LeafSizeHelper(b *testing.B, base Word, size int) {
for d := 1; d <= 10000; d *= 10 { for d := 1; d <= 10000; d *= 10 {
b.StopTimer() b.StopTimer()
var z nat var z nat
z = z.expWW(base, Word(d)) // build target number z = z.expWW(Word(base), Word(d)) // build target number
_ = z.string(lowercaseDigits[:base]) // warm divisor cache _ = z.itoa(base) // warm divisor cache
b.StartTimer() b.StartTimer()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
_ = z.string(lowercaseDigits[:base]) _ = z.itoa(base)
} }
} }
...@@ -408,13 +405,13 @@ func resetTable(table []divisor) { ...@@ -408,13 +405,13 @@ func resetTable(table []divisor) {
} }
func TestStringPowers(t *testing.T) { func TestStringPowers(t *testing.T) {
var b, p Word var p Word
for b = 2; b <= 16; b++ { for b := 2; b <= 16; b++ {
for p = 0; p <= 512; p++ { for p = 0; p <= 512; p++ {
x := nat(nil).expWW(b, p) x := nat(nil).expWW(Word(b), p)
xs := x.string(lowercaseDigits[:b]) xs := x.itoa(b)
xs2 := toString(x, lowercaseDigits[:b]) xs2 := itoa(x, b)
if xs != xs2 { if !bytes.Equal(xs, xs2) {
t.Errorf("failed at %d ** %d in base %d: %s != %s", b, p, b, xs, xs2) t.Errorf("failed at %d ** %d in base %d: %s != %s", b, p, b, xs, xs2)
} }
} }
......
...@@ -190,7 +190,7 @@ func scanExponent(r io.ByteScanner, binExpOk bool) (exp int64, base int, err err ...@@ -190,7 +190,7 @@ func scanExponent(r io.ByteScanner, binExpOk bool) (exp int64, base int, err err
func (x *Rat) String() string { func (x *Rat) String() string {
s := "/1" s := "/1"
if len(x.b.abs) != 0 { if len(x.b.abs) != 0 {
s = "/" + x.b.abs.decimalString() s = "/" + string(x.b.abs.itoa(10))
} }
return x.a.String() + s return x.a.String() + s
} }
...@@ -237,13 +237,13 @@ func (x *Rat) FloatString(prec int) string { ...@@ -237,13 +237,13 @@ func (x *Rat) FloatString(prec int) string {
} }
} }
s := q.decimalString() s := string(q.itoa(10))
if x.a.neg { if x.a.neg {
s = "-" + s s = "-" + s
} }
if prec > 0 { if prec > 0 {
rs := r.decimalString() rs := string(r.itoa(10))
leadingZeros := prec - len(rs) leadingZeros := prec - len(rs)
s += "." + strings.Repeat("0", leadingZeros) + rs s += "." + strings.Repeat("0", leadingZeros) + rs
} }
......
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