Skip to content

Commit

Permalink
Performance optimisations
Browse files Browse the repository at this point in the history
  • Loading branch information
dop251 committed Apr 24, 2020
1 parent d0b8fda commit 8acb9ed
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 25 deletions.
4 changes: 2 additions & 2 deletions ftoa/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ func stuffBits(bits []byte, offset int, val uint32) {
bits[offset+3] = byte(val)
}

func d2b(d float64) (b *big.Int, e, bits int) {
func d2b(d float64, bi *big.Int) (e, bits int) {
dBits := math.Float64bits(d)
d0 := uint32(dBits >> 32)
d1 := uint32(dBits)
Expand Down Expand Up @@ -144,6 +144,6 @@ func d2b(d float64) (b *big.Int, e, bits int) {
e = de - bias - (p - 1) + 1 + k
bits = 32*i - hi0bits(z)
}
b = (&big.Int{}).SetBytes(dbl_bits)
bi.SetBytes(dbl_bits)
return
}
3 changes: 2 additions & 1 deletion ftoa/ftobasestr.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ func FToBaseStr(num float64, radix int) string {
word0 := uint32(dBits >> 32)
word1 := uint32(dBits)

b, e, _ := d2b(df)
b := new(big.Int)
e, _ := d2b(df, b)
// JS_ASSERT(e < 0);
/* At this point df = b * 2^e. e must be less than zero because 0 < df < 1. */

Expand Down
83 changes: 61 additions & 22 deletions ftoa/ftostr.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ var (
big5 = big.NewInt(5)
big10 = big.NewInt(10)

p05 = []*big.Int{big5, big.NewInt(25), big.NewInt(125)}
pow5Cache [7]*big.Int

dtoaModes = []int{
ModeStandard: 0,
ModeStandardExponential: 0,
Expand Down Expand Up @@ -100,7 +103,8 @@ func ftoa(d float64, mode int, biasUp bool, ndigits int, buf []byte) ([]byte, in
if sign {
buf = append(buf, '-')
}
b, be, bbits := d2b(d)
b := new(big.Int)
be, bbits := d2b(d, b)
i := int((word0 >> exp_shift1) & (exp_mask >> exp_shift1))
var d2 float64
var denorm bool
Expand Down Expand Up @@ -534,22 +538,21 @@ func ftoa(d float64, mode int, biasUp bool, ndigits int, buf []byte) ([]byte, in
}
/* mlo/S = maximum acceptable error, divided by 10^k, if the output is less than d. */
/* mhi/S = maximum acceptable error, divided by 10^k, if the output is greater than d. */

var z, delta big.Int
for i = 1; ; i++ {
z := new(big.Int)
z.DivMod(b, S, b)
dig = byte(z.Int64() + '0')
/* Do we yet have the shortest decimal string
* that will round to d?
*/
j = b.Cmp(mlo)
/* j is b/S compared with mlo/S. */
delta := new(big.Int).Sub(S, mhi)
delta.Sub(S, mhi)
var j1 int
if delta.Sign() <= 0 {
j1 = 1
} else {
j1 = b.Cmp(delta)
j1 = b.Cmp(&delta)
}
/* j1 is b/S compared with 1 - mhi/S. */
if (j1 == 0) && (mode == 0) && ((_word1(d) & 1) == 0) {
Expand All @@ -560,13 +563,13 @@ func ftoa(d float64, mode int, biasUp bool, ndigits int, buf []byte) ([]byte, in
k++
buf = append(buf, '1')
}
return buf, int(k + 1)
return buf, k + 1
}
if j > 0 {
dig++
}
buf = append(buf, dig)
return buf, int(k + 1)
return buf, k + 1
}
if (j < 0) || ((j == 0) && (mode == 0) && ((_word1(d) & 1) == 0)) {
if j1 > 0 {
Expand All @@ -583,28 +586,25 @@ func ftoa(d float64, mode int, biasUp bool, ndigits int, buf []byte) ([]byte, in
k++
buf = append(buf, '1')
}
return buf, int(k + 1)
return buf, k + 1
}
}
}
buf = append(buf, dig)
return buf, int(k + 1)
return buf, k + 1
}
if j1 > 0 {
if dig == '9' { /* possible if i == 1 */
// round_9_up:
// *s++ = '9';
// goto roundoff;
buf = append(buf, '9')
buf, flag := roundOff(buf)
if flag {
k++
buf = append(buf, '1')
}
return buf, int(k + 1)
return buf, k + 1
}
buf = append(buf, dig+1)
return buf, int(k + 1)
return buf, k + 1
}
buf = append(buf, dig)
if i == ilim {
Expand All @@ -619,9 +619,8 @@ func ftoa(d float64, mode int, biasUp bool, ndigits int, buf []byte) ([]byte, in
}
}
} else {
var z big.Int
for i = 1; ; i++ {
// (char)(dig = quorem(b,S) + '0');
z := new(big.Int)
z.DivMod(b, S, b)
dig = byte(z.Int64() + '0')
buf = append(buf, dig)
Expand All @@ -642,13 +641,13 @@ func ftoa(d float64, mode int, biasUp bool, ndigits int, buf []byte) ([]byte, in
if flag {
k++
buf = append(buf, '1')
return buf, int(k + 1)
return buf, k + 1
}
} else {
buf = stripTrailingZeroes(buf)
}

return buf, int(k + 1)
return buf, k + 1
}

func insert(b []byte, p int, c byte) []byte {
Expand All @@ -658,6 +657,16 @@ func insert(b []byte, p int, c byte) []byte {
return b
}

func expand(b []byte, delta int) []byte {
newLen := len(b) + delta
if newLen <= cap(b) {
return b[:newLen]
}
b1 := make([]byte, newLen)
copy(b1, b)
return b1
}

func FToStr(d float64, mode FToStrMode, precision int, buffer []byte) []byte {
if mode == ModeFixed && (d >= 1e21 || d <= -1e21) {
mode = ModeStandard
Expand Down Expand Up @@ -736,10 +745,13 @@ func FToStr(d float64, mode FToStrMode, precision int, buffer []byte) []byte {
if sign {
o = 1
}
for i := 0; i < 1-decPt; i++ {
buffer = insert(buffer, o, '0')
buffer = expand(buffer, 2-decPt)
copy(buffer[o+2-decPt:], buffer[o:])
buffer[o] = '0'
buffer[o+1] = '.'
for i := o + 2; i < o+2-decPt; i++ {
buffer[i] = '0'
}
buffer = insert(buffer, o+1, '.')
}
}
}
Expand Down Expand Up @@ -792,8 +804,26 @@ func stripTrailingZeroes(buf []byte) []byte {
}

/* Set b = b * 5^k. k must be nonnegative. */
// XXXX the C version built a cache of these
func pow5mult(b *big.Int, k int) *big.Int {
if k < (1 << (len(pow5Cache) + 2)) {
i := k & 3
if i != 0 {
b.Mul(b, p05[i-1])
}
k >>= 2
i = 0
for {
if k&1 != 0 {
b.Mul(b, pow5Cache[i])
}
k >>= 1
if k == 0 {
break
}
i++
}
return b
}
return b.Mul(b, new(big.Int).Exp(big5, big.NewInt(int64(k)), nil))
}

Expand All @@ -812,3 +842,12 @@ func roundOff(buf []byte) ([]byte, bool) {
}
return buf[:stop], true
}

func init() {
p := big.NewInt(625)
pow5Cache[0] = p
for i := 1; i < len(pow5Cache); i++ {
p = new(big.Int).Mul(p, p)
pow5Cache[i] = p
}
}
34 changes: 34 additions & 0 deletions ftoa/ftostr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package ftoa

import (
"math"
"strconv"
"testing"
)

Expand Down Expand Up @@ -31,7 +32,40 @@ func TestDtostr(t *testing.T) {
testFToStr(8.85, ModeExponential, 2, "8.8e+0", t)
testFToStr(885, ModeExponential, 2, "8.9e+2", t)
testFToStr(25, ModeExponential, 1, "3e+1", t)
testFToStr(1e-6, ModeFixed, 7, "0.0000010", t)
testFToStr(math.Inf(1), ModeStandard, 0, "Infinity", t)
testFToStr(math.NaN(), ModeStandard, 0, "NaN", t)
testFToStr(math.SmallestNonzeroFloat64, ModeExponential, 40, "4.940656458412465441765687928682213723651e-324", t)
}

func BenchmarkDtostrSmall(b *testing.B) {
var buf [128]byte
b.ReportAllocs()
for i := 0; i < b.N; i++ {
FToStr(math.Pi, ModeExponential, 0, buf[:0])
}
}

func BenchmarkDtostrBig(b *testing.B) {
var buf [128]byte
b.ReportAllocs()
for i := 0; i < b.N; i++ {
FToStr(math.SmallestNonzeroFloat64, ModeExponential, 40, buf[:0])
}
}

func BenchmarkAppendFloatBig(b *testing.B) {
var buf [128]byte
b.ReportAllocs()
for i := 0; i < b.N; i++ {
strconv.AppendFloat(buf[:0], math.SmallestNonzeroFloat64, 'e', 40, 64)
}
}

func BenchmarkAppendFloatSmall(b *testing.B) {
var buf [128]byte
b.ReportAllocs()
for i := 0; i < b.N; i++ {
strconv.AppendFloat(buf[:0], math.Pi, 'e', -1, 64)
}
}

0 comments on commit 8acb9ed

Please sign in to comment.