Skip to content

Commit e2bc4ac

Browse files
authored
Merge pull request #125 from miretskiy/decimal
apd: Improve string conversion efficiency
2 parents e2030eb + 9d47f6a commit e2bc4ac

File tree

2 files changed

+30
-10
lines changed

2 files changed

+30
-10
lines changed

bench_test.go

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@ import (
2424
// runBenches benchmarks a given function on random decimals on combinations of
2525
// three parameters:
2626
//
27-
// precision: desired output precision
28-
// inScale: the scale of the input decimal: the absolute value will be between
29-
// 10^inScale and 10^(inScale+1)
30-
// inNumDigits: number of digits in the input decimal; if negative the number
31-
// will be negative and the number of digits are the absolute value.
27+
// precision: desired output precision
28+
// inScale: the scale of the input decimal: the absolute value will be between
29+
// 10^inScale and 10^(inScale+1)
30+
// inNumDigits: number of digits in the input decimal; if negative the number
31+
// will be negative and the number of digits are the absolute value.
3232
func runBenches(
3333
b *testing.B, precision, inScale, inNumDigits []int, fn func(*testing.B, *Context, *Decimal),
3434
) {
@@ -103,3 +103,22 @@ func BenchmarkLn(b *testing.B) {
103103
},
104104
)
105105
}
106+
107+
func BenchmarkDecimalString(b *testing.B) {
108+
rng := rand.New(rand.NewSource(461210934723948))
109+
corpus := func() []Decimal {
110+
res := make([]Decimal, 8192)
111+
for i := range res {
112+
_, err := res[i].SetFloat64(rng.Float64())
113+
if err != nil {
114+
b.Fatal(err)
115+
}
116+
}
117+
return res
118+
}()
119+
b.ResetTimer()
120+
b.ReportAllocs()
121+
for i := 0; i < b.N; i++ {
122+
_ = corpus[rng.Intn(len(corpus))].String()
123+
}
124+
}

format.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ import (
2626
// zeros (the 'g' format avoids the use of 'f' in this case). All other
2727
// formats always show the exact precision of the Decimal.
2828
func (d *Decimal) Text(format byte) string {
29-
cap := 10 // TODO(gri) determine a good/better value here
30-
return string(d.Append(make([]byte, 0, cap), format))
29+
var buf [16]byte
30+
return string(d.Append(buf[:0], format))
3131
}
3232

3333
// String formats x like x.Text('G'). It matches the to-scientific-string
@@ -57,7 +57,8 @@ func (d *Decimal) Append(buf []byte, fmt byte) []byte {
5757
return append(buf, "unknown"...)
5858
}
5959

60-
digits := d.Coeff.String()
60+
var scratch [16]byte
61+
digits := d.Coeff.Append(scratch[:0], 10)
6162
switch fmt {
6263
case 'e', 'E':
6364
return fmtE(buf, fmt, d, digits)
@@ -83,7 +84,7 @@ func (d *Decimal) Append(buf []byte, fmt byte) []byte {
8384
}
8485

8586
// %e: d.ddddde±d
86-
func fmtE(buf []byte, fmt byte, d *Decimal, digits string) []byte {
87+
func fmtE(buf []byte, fmt byte, d *Decimal, digits []byte) []byte {
8788
adj := int64(d.Exponent) + int64(len(digits)) - 1
8889
buf = append(buf, digits[0])
8990
if len(digits) > 1 {
@@ -103,7 +104,7 @@ func fmtE(buf []byte, fmt byte, d *Decimal, digits string) []byte {
103104
}
104105

105106
// %f: ddddddd.ddddd
106-
func fmtF(buf []byte, d *Decimal, digits string) []byte {
107+
func fmtF(buf []byte, d *Decimal, digits []byte) []byte {
107108
if d.Exponent < 0 {
108109
if left := -int(d.Exponent) - len(digits); left >= 0 {
109110
buf = append(buf, "0."...)

0 commit comments

Comments
 (0)