Skip to content

Commit e937eee

Browse files
committed
math/big: removed more unnecessary string conversions
- renamed (nat) itoa to utoa (since that's what it is) - added (nat) itoa that takes a sign parameter; this helps removing a few string copies - used buffers instead of string+ in Rat conversions Change-Id: I6b37a6b39557ae311cafdfe5c4a26e9246bde1a9 Reviewed-on: https://go-review.googlesource.com/14995 Reviewed-by: Alan Donovan <[email protected]>
1 parent 8d701f0 commit e937eee

7 files changed

Lines changed: 62 additions & 44 deletions

File tree

src/math/big/decimal.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ func (x *decimal) init(m nat, shift int) {
8080
}
8181

8282
// Convert mantissa into decimal representation.
83-
s := m.itoa(10)
83+
s := m.utoa(10)
8484
n := len(s)
8585
x.exp = n
8686
// Trim trailing zeros; instead the exponent is tracking

src/math/big/ftoa.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ func roundShortest(d *decimal, x *Float) {
165165
// Approach: All numbers in the interval [x - 1/2ulp, x + 1/2ulp]
166166
// (possibly exclusive) round to x for the given precision of x.
167167
// Compute the lower and upper bound in decimal form and find the
168-
// the shortest decimal number d such that lower <= d <= upper.
168+
// shortest decimal number d such that lower <= d <= upper.
169169

170170
// TODO(gri) strconv/ftoa.do describes a shortcut in some cases.
171171
// See if we can use it (in adjusted form) here as well.
@@ -323,7 +323,7 @@ func (x *Float) fmtB(buf []byte) []byte {
323323
m = nat(nil).shr(m, uint(w-x.prec))
324324
}
325325

326-
buf = append(buf, m.itoa(10)...)
326+
buf = append(buf, m.utoa(10)...)
327327
buf = append(buf, 'p')
328328
e := int64(x.exp) - int64(x.prec)
329329
if e >= 0 {
@@ -357,7 +357,7 @@ func (x *Float) fmtP(buf []byte) []byte {
357357
m = m[i:]
358358

359359
buf = append(buf, "0x."...)
360-
buf = append(buf, bytes.TrimRight(m.itoa(16), "0")...)
360+
buf = append(buf, bytes.TrimRight(m.utoa(16), "0")...)
361361
buf = append(buf, 'p')
362362
if x.exp >= 0 {
363363
buf = append(buf, '+')

src/math/big/intconv.go

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,7 @@ func (x *Int) Text(base int) string {
2626
if x == nil {
2727
return "<nil>"
2828
}
29-
s := string(x.abs.itoa(base))
30-
if x.neg {
31-
s = "-" + s
32-
}
33-
return s
29+
return string(x.abs.itoa(x.neg, base))
3430
}
3531

3632
// Append appends the string representation of x, as generated by
@@ -39,10 +35,7 @@ func (x *Int) Append(buf []byte, base int) []byte {
3935
if x == nil {
4036
return append(buf, "<nil>"...)
4137
}
42-
if x.neg {
43-
buf = append(buf, '-')
44-
}
45-
return append(buf, x.abs.itoa(base)...)
38+
return append(buf, x.abs.itoa(x.neg, base)...)
4639
}
4740

4841
func (x *Int) String() string {
@@ -117,7 +110,7 @@ func (x *Int) Format(s fmt.State, ch rune) {
117110
}
118111
}
119112

120-
digits := x.abs.itoa(base)
113+
digits := x.abs.utoa(base)
121114
if ch == 'X' {
122115
// faster than bytes.ToUpper
123116
for i, d := range digits {

src/math/big/nat_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ var mulRangesN = []struct {
158158

159159
func TestMulRangeN(t *testing.T) {
160160
for i, r := range mulRangesN {
161-
prod := string(nat(nil).mulRange(r.a, r.b).itoa(10))
161+
prod := string(nat(nil).mulRange(r.a, r.b).utoa(10))
162162
if prod != r.prod {
163163
t.Errorf("#%d: got %s; want %s", i, prod, r.prod)
164164
}
@@ -326,7 +326,7 @@ func TestTrailingZeroBits(t *testing.T) {
326326
for i := uint(0); i <= 3*_W; i++ {
327327
n := y.trailingZeroBits()
328328
if n != i {
329-
t.Errorf("got 0x%s.trailingZeroBits() = %d; want %d", y.itoa(16), n, i)
329+
t.Errorf("got 0x%s.trailingZeroBits() = %d; want %d", y.utoa(16), n, i)
330330
}
331331
y = y.shl(y, 1)
332332
}
@@ -388,7 +388,7 @@ func TestMontgomery(t *testing.T) {
388388
z := nat(nil).montgomery(x, y, m, k0, len(m))
389389
z = z.norm()
390390
if z.cmp(out) != 0 {
391-
t.Errorf("#%d got %s want %s", i, z.itoa(10), out.itoa(10))
391+
t.Errorf("#%d got %s want %s", i, z.utoa(10), out.utoa(10))
392392
}
393393
}
394394
}
@@ -429,7 +429,7 @@ func TestExpNN(t *testing.T) {
429429

430430
z := nat(nil).expNN(x, y, m)
431431
if z.cmp(out) != 0 {
432-
t.Errorf("#%d got %s want %s", i, z.itoa(10), out.itoa(10))
432+
t.Errorf("#%d got %s want %s", i, z.utoa(10), out.utoa(10))
433433
}
434434
}
435435
}
@@ -486,7 +486,7 @@ var fiboNums = []string{
486486
func TestFibo(t *testing.T) {
487487
for i, want := range fiboNums {
488488
n := i * 10
489-
got := string(fibo(n).itoa(10))
489+
got := string(fibo(n).utoa(10))
490490
if got != want {
491491
t.Errorf("fibo(%d) failed: got %s want %s", n, got, want)
492492
}

src/math/big/natconv.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -234,9 +234,14 @@ func (z nat) scan(r io.ByteScanner, base int, fracOk bool) (res nat, b, count in
234234
return
235235
}
236236

237-
// itoa converts x to an ASCII representation in the given base;
237+
// utoa converts x to an ASCII representation in the given base;
238238
// base must be between 2 and MaxBase, inclusive.
239-
func (x nat) itoa(base int) []byte {
239+
func (x nat) utoa(base int) []byte {
240+
return x.itoa(false, base)
241+
}
242+
243+
// itoa is like utoa but it prepends a '-' if neg && x != 0.
244+
func (x nat) itoa(neg bool, base int) []byte {
240245
if base < 2 || base > MaxBase {
241246
panic("invalid base")
242247
}
@@ -249,6 +254,9 @@ func (x nat) itoa(base int) []byte {
249254

250255
// allocate buffer for conversion
251256
i := int(float64(x.bitLen())/math.Log2(float64(base))) + 1 // off by 1 at most
257+
if neg {
258+
i++
259+
}
252260
s := make([]byte, i)
253261

254262
// convert power of two and non power of two bases separately
@@ -315,6 +323,11 @@ func (x nat) itoa(base int) []byte {
315323
}
316324
}
317325

326+
if neg {
327+
i--
328+
s[i] = '-'
329+
}
330+
318331
return s[i:]
319332
}
320333

src/math/big/natconv_test.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -61,14 +61,14 @@ func TestString(t *testing.T) {
6161
defer func() {
6262
panicStr = recover().(string)
6363
}()
64-
natOne.itoa(1)
64+
natOne.utoa(1)
6565
}()
6666
if panicStr != "invalid base" {
6767
t.Errorf("expected panic for invalid base")
6868
}
6969

7070
for _, a := range strTests {
71-
s := string(a.x.itoa(a.b))
71+
s := string(a.x.utoa(a.b))
7272
if s != a.s {
7373
t.Errorf("string%+v\n\tgot s = %s; want %s", a, s, a.s)
7474
}
@@ -234,7 +234,7 @@ func TestScanPi(t *testing.T) {
234234
if err != nil {
235235
t.Errorf("scanning pi: %s", err)
236236
}
237-
if s := string(z.itoa(10)); s != pi {
237+
if s := string(z.utoa(10)); s != pi {
238238
t.Errorf("scanning pi: got %s", s)
239239
}
240240
}
@@ -263,12 +263,12 @@ func BenchmarkScanPi(b *testing.B) {
263263
func BenchmarkStringPiParallel(b *testing.B) {
264264
var x nat
265265
x, _, _, _ = x.scan(strings.NewReader(pi), 0, false)
266-
if string(x.itoa(10)) != pi {
266+
if string(x.utoa(10)) != pi {
267267
panic("benchmark incorrect: conversion failed")
268268
}
269269
b.RunParallel(func(pb *testing.PB) {
270270
for pb.Next() {
271-
x.itoa(10)
271+
x.utoa(10)
272272
}
273273
})
274274
}
@@ -302,7 +302,7 @@ func ScanHelper(b *testing.B, base int, x, y Word) {
302302
var z nat
303303
z = z.expWW(x, y)
304304

305-
s := z.itoa(base)
305+
s := z.utoa(base)
306306
if t := itoa(z, base); !bytes.Equal(s, t) {
307307
b.Fatalf("scanning: got %s; want %s", s, t)
308308
}
@@ -341,11 +341,11 @@ func StringHelper(b *testing.B, base int, x, y Word) {
341341
b.StopTimer()
342342
var z nat
343343
z = z.expWW(x, y)
344-
z.itoa(base) // warm divisor cache
344+
z.utoa(base) // warm divisor cache
345345
b.StartTimer()
346346

347347
for i := 0; i < b.N; i++ {
348-
_ = z.itoa(base)
348+
_ = z.utoa(base)
349349
}
350350
}
351351

@@ -380,11 +380,11 @@ func LeafSizeHelper(b *testing.B, base, size int) {
380380
b.StopTimer()
381381
var z nat
382382
z = z.expWW(Word(base), Word(d)) // build target number
383-
_ = z.itoa(base) // warm divisor cache
383+
_ = z.utoa(base) // warm divisor cache
384384
b.StartTimer()
385385

386386
for i := 0; i < b.N; i++ {
387-
_ = z.itoa(base)
387+
_ = z.utoa(base)
388388
}
389389
}
390390

@@ -409,7 +409,7 @@ func TestStringPowers(t *testing.T) {
409409
for b := 2; b <= 16; b++ {
410410
for p = 0; p <= 512; p++ {
411411
x := nat(nil).expWW(Word(b), p)
412-
xs := x.itoa(b)
412+
xs := x.utoa(b)
413413
xs2 := itoa(x, b)
414414
if !bytes.Equal(xs, xs2) {
415415
t.Errorf("failed at %d ** %d in base %d: %s != %s", b, p, b, xs, xs2)

src/math/big/ratconv.go

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -188,11 +188,15 @@ func scanExponent(r io.ByteScanner, binExpOk bool) (exp int64, base int, err err
188188

189189
// String returns a string representation of x in the form "a/b" (even if b == 1).
190190
func (x *Rat) String() string {
191-
s := "/1"
191+
var buf []byte
192+
buf = x.a.Append(buf, 10)
193+
buf = append(buf, '/')
192194
if len(x.b.abs) != 0 {
193-
s = "/" + string(x.b.abs.itoa(10))
195+
buf = x.b.Append(buf, 10)
196+
} else {
197+
buf = append(buf, '1')
194198
}
195-
return x.a.String() + s
199+
return string(buf)
196200
}
197201

198202
// RatString returns a string representation of x in the form "a/b" if b != 1,
@@ -208,12 +212,17 @@ func (x *Rat) RatString() string {
208212
// digits of precision after the decimal point. The last digit is rounded to
209213
// nearest, with halves rounded away from zero.
210214
func (x *Rat) FloatString(prec int) string {
215+
var buf []byte
216+
211217
if x.IsInt() {
212-
s := x.a.String()
218+
buf = x.a.Append(buf, 10)
213219
if prec > 0 {
214-
s += "." + strings.Repeat("0", prec)
220+
buf = append(buf, '.')
221+
for i := prec; i > 0; i-- {
222+
buf = append(buf, '0')
223+
}
215224
}
216-
return s
225+
return string(buf)
217226
}
218227
// x.b.abs != 0
219228

@@ -237,16 +246,19 @@ func (x *Rat) FloatString(prec int) string {
237246
}
238247
}
239248

240-
s := string(q.itoa(10))
241249
if x.a.neg {
242-
s = "-" + s
250+
buf = append(buf, '-')
243251
}
252+
buf = append(buf, q.utoa(10)...) // itoa ignores sign if q == 0
244253

245254
if prec > 0 {
246-
rs := string(r.itoa(10))
247-
leadingZeros := prec - len(rs)
248-
s += "." + strings.Repeat("0", leadingZeros) + rs
255+
buf = append(buf, '.')
256+
rs := r.utoa(10)
257+
for i := prec - len(rs); i > 0; i-- {
258+
buf = append(buf, '0')
259+
}
260+
buf = append(buf, rs...)
249261
}
250262

251-
return s
263+
return string(buf)
252264
}

0 commit comments

Comments
 (0)