Skip to content
This repository has been archived by the owner on Sep 1, 2021. It is now read-only.

Commit

Permalink
Use bit twiddling hack for decimalLen64
Browse files Browse the repository at this point in the history
This helps a little for small, common integers.

name                                     old time/op  new time/op  delta
AppendFloat32/0e+00-12                   2.96ns ± 2%  2.96ns ± 1%    ~     (p=0.992 n=7+7)
AppendFloat32/1e+00-12                   13.3ns ± 2%  13.1ns ± 2%  -1.82%  (p=0.016 n=7+7)
AppendFloat32/3e-01-12                   32.6ns ± 1%  32.4ns ± 1%    ~     (p=0.179 n=7+7)
AppendFloat32/1e+06-12                   17.6ns ± 1%  16.9ns ± 1%  -3.55%  (p=0.000 n=7+6)
AppendFloat32/-1.2345e+02-12             34.2ns ± 1%  34.1ns ± 1%    ~     (p=0.362 n=7+7)
AppendFloat64/0e+00-12                   3.29ns ± 1%  3.28ns ± 1%    ~     (p=0.368 n=7+7)
AppendFloat64/1e+00-12                   15.2ns ± 2%  14.5ns ± 1%  -4.19%  (p=0.001 n=7+6)
AppendFloat64/3e-01-12                   50.8ns ± 0%  49.0ns ± 1%  -3.51%  (p=0.000 n=6+7)
AppendFloat64/1e+06-12                   21.0ns ± 1%  21.1ns ± 2%    ~     (p=0.400 n=6+7)
AppendFloat64/-1.2345e+02-12             50.1ns ± 0%  49.0ns ± 1%  -2.06%  (p=0.004 n=5+6)
AppendFloat64/6.226662346353213e-309-12  39.9ns ± 1%  40.3ns ± 1%  +1.07%  (p=0.018 n=7+7)
  • Loading branch information
cespare committed Jan 13, 2019
1 parent 390dcd6 commit f790d55
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 41 deletions.
7 changes: 7 additions & 0 deletions ryu.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,13 @@ func pow5Bits(e int32) int32 {

// FIXME(caleb): Document how these are optimized.

func boolToInt(b bool) int {
if b {
return 1
}
return 0
}

func boolToUint32(b bool) uint32 {
if b {
return 1
Expand Down
1 change: 1 addition & 0 deletions ryu32.go
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ func float32ToDecimal(mant, exp uint32) dec32 {
func decimalLen32(u uint32) int {
// Function precondition: u is not a 10-digit number.
// (9 digits are sufficient for round-tripping.)
// This benchmarked faster than the log2 approach used for uint64s.
assert(u < 1000000000, "too big")
switch {
case u >= 100000000:
Expand Down
67 changes: 26 additions & 41 deletions ryu64.go
Original file line number Diff line number Diff line change
Expand Up @@ -292,48 +292,33 @@ func float64ToDecimal(mant, exp uint64) dec64 {
return dec64{m: out, e: e10 + removed}
}

var powersOf10 = [...]uint64{
1e0,
1e1,
1e2,
1e3,
1e4,
1e5,
1e6,
1e7,
1e8,
1e9,
1e10,
1e11,
1e12,
1e13,
1e14,
1e15,
1e16,
1e17,
// We only need to find the length of at most 17 digit numbers.
}

func decimalLen64(u uint64) int {
// This is slightly faster than a loop. FIXME: Confirm.
// The average output length is 16.38 digits, so we check high-to-low.
// Function precondition: v is not an 18, 19, or 20-digit number.
// (17 digits are sufficient for round-tripping.)
assert(u < 100000000000000000, "too big")
switch {
case u >= 10000000000000000:
return 17
case u >= 1000000000000000:
return 16
case u >= 100000000000000:
return 15
case u >= 10000000000000:
return 14
case u >= 1000000000000:
return 13
case u >= 100000000000:
return 12
case u >= 10000000000:
return 11
case u >= 1000000000:
return 10
case u >= 100000000:
return 9
case u >= 10000000:
return 8
case u >= 1000000:
return 7
case u >= 100000:
return 6
case u >= 10000:
return 5
case u >= 1000:
return 4
case u >= 100:
return 3
case u >= 10:
return 2
default:
return 1
}
// http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10
log2 := 64 - bits.LeadingZeros64(u) - 1
t := (log2 + 1) * 1233 >> 12
return t - boolToInt(u < powersOf10[t]) + 1
}

func mulShift64(m uint64, mul uint128, shift int32) uint64 {
Expand Down
24 changes: 24 additions & 0 deletions ryu_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package ryu

import (
"math"
"math/big"
"math/rand"
"strconv"
"testing"
Expand Down Expand Up @@ -219,3 +220,26 @@ func BenchmarkStrconvAppendFloat64(b *testing.B) {
})
}
}

func TestDecimalLen(t *testing.T) {
for n := uint64(1); n < 1000; n++ {
testDecimalLen(t, n)
}
for i := 0; i < 1e5; i++ {
n := uint64(rand.Intn(99999999999999999) + 1)
testDecimalLen(t, n)
}
}

func testDecimalLen(t *testing.T, n uint64) {
t.Helper()
want := len(big.NewInt(int64(n)).String()) // n fits into int64
if got := decimalLen64(n); got != want {
t.Fatalf("decimalLen64(%d): got %d; want %d", n, got, want)
}
if n < math.MaxUint32 {
if got := decimalLen32(uint32(n)); got != want {
t.Fatalf("decimalLen32(%d): got %d; want %d", n, got, want)
}
}
}

0 comments on commit f790d55

Please sign in to comment.