Skip to content

Commit

Permalink
dec: fix shl/shr, add string conversion to/from dec
Browse files Browse the repository at this point in the history
The loops in shr/shl were reversed.
  • Loading branch information
db47h committed May 5, 2020
1 parent 515766f commit 9fea708
Show file tree
Hide file tree
Showing 8 changed files with 695 additions and 44 deletions.
58 changes: 57 additions & 1 deletion dec.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ const (
// representation of 0 is the empty or nil slice (length = 0).
type dec []Word

var (
decOne = dec{1}
)

func (z dec) clear() {
for i := range z {
z[i] = 0
Expand All @@ -48,7 +52,7 @@ func (z dec) norm() dec {
// digits returns the number of digits of x.
func (x dec) digits() uint {
if i := len(x) - 1; i >= 0 {
return uint(i*_W) + decDigits(uint(x[i]))
return uint(i*_WD) + decDigits(uint(x[i]))
}
return 0
}
Expand Down Expand Up @@ -138,6 +142,33 @@ func (x dec) sticky(i uint) uint {
return 0
}

func (x dec) cmp(y dec) (r int) {
m := len(x)
n := len(y)
if m != n || m == 0 {
switch {
case m < n:
r = -1
case m > n:
r = 1
}
return
}

i := m - 1
for i > 0 && x[i] == y[i] {
i--
}

switch {
case x[i] < y[i]:
r = -1
case x[i] > y[i]:
r = 1
}
return
}

// q = (x-r)/y, with 0 <= r < y
func (z dec) divW(x dec, y Word) (q dec, r Word) {
m := len(x)
Expand Down Expand Up @@ -190,12 +221,37 @@ func (z dec) shl(x dec, s uint) dec {

n := m + int(s/_WD)
z = z.make(n + 1)
// TODO(db47h): optimize and bench shifts when s%_WD == 0
z[n] = shl10VU(z[n-m:n], x, s%_WD)
z[0 : n-m].clear()

return z.norm()
}

// z = x >> s
func (z dec) shr(x dec, s uint) dec {
if s == 0 {
if same(z, x) {
return z
}
if !alias(z, x) {
return z.set(x)
}
}

m := len(x)
n := m - int(s/_WD)
if n <= 0 {
return z[:0]
}
// n > 0

z = z.make(n)
shr10VU(z, x[m-n:], s%_WD)

return z.norm()
}

// getDec returns a *dec of len n. The contents may not be zero.
// The pool holds *dec to avoid allocation when converting to interface{}.
func getDec(n int) *dec {
Expand Down
84 changes: 53 additions & 31 deletions dec_arith.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,25 +32,45 @@ func decDigits(x uint) (n uint) {

// shl10VU sets z to x*(10**s), s < _WD
func shl10VU(z, x dec, s uint) (r Word) {
m := pow10(s)
for i := 0; i < len(z) && i < len(x); i++ {
h, l := bits.Mul(uint(x[i]), m)
h, l = bits.Div(h, l, _BD)
z[i] = Word(l) + r
r = Word(h)
if s == 0 {
copy(z, x)
return
}
if len(z) == 0 || len(x) == 0 {
return
}
d, m := Word(pow10(_WD-s)), Word(pow10(s))
var h, l Word
r, l = divWW(0, x[len(x)-1], d)
for i := len(z) - 1; i > 0; i-- {
t := l
h, l = divWW(0, x[i-1], d)
z[i] = t*m + h
}
z[0] = l * m

return r
}

// shr10VU sets z to x/(10**s)
func shr10VU(z, x dec, s uint) (r Word) {
if s == 0 {
copy(z, x)
return
}
if len(z) == 0 || len(x) == 0 {
return
}

var h, l Word
d, m := Word(pow10(s)), Word(pow10(_WD-s))
for i := len(x) - 1; i >= 0; i-- {
var q Word
rm := r * m
q, r = x[i]/d, x[i]%d
z[i] = rm + q
h, r = divWW(0, x[0], Word(d))
for i := 1; i < len(z) && i < len(x); i++ {
t := h
h, l = divWW(0, x[i], d)
z[i-1] = t + l*m
}
z[len(z)-1] = h
return r
}

Expand Down Expand Up @@ -106,36 +126,38 @@ func dec64TrailingZeros(n uint64) uint {
}

var decMaxPow32 = [...]uint32{
0, 0, 0, 0,
536870912, 29, 387420489, 18, 268435456, 14, 244140625, 12, 362797056, 11,
282475249, 10, 134217728, 9, 387420489, 9, 1000000000, 9, 214358881, 8,
429981696, 8, 815730721, 8, 105413504, 7, 170859375, 7, 268435456, 7,
410338673, 7, 612220032, 7, 893871739, 7, 64000000, 6, 85766121, 6,
113379904, 6, 148035889, 6, 191102976, 6, 244140625, 6, 308915776, 6,
387420489, 6, 481890304, 6, 594823321, 6, 729000000, 6, 887503681, 6,
33554432, 5, 39135393, 5, 45435424, 5, 52521875, 5, 60466176, 5,
0, 0, 0, 0, 536870912, 29, 387420489, 18, 268435456, 14, 244140625, 12, 362797056, 11,
282475249, 10, 134217728, 9, 387420489, 9, 1000000000, 9, 214358881, 8, 429981696, 8, 815730721, 8,
105413504, 7, 170859375, 7, 268435456, 7, 410338673, 7, 612220032, 7, 893871739, 7, 64000000, 6,
85766121, 6, 113379904, 6, 148035889, 6, 191102976, 6, 244140625, 6, 308915776, 6, 387420489, 6,
481890304, 6, 594823321, 6, 729000000, 6, 887503681, 6, 33554432, 5, 39135393, 5, 45435424, 5,
52521875, 5, 60466176, 5, 69343957, 5, 79235168, 5, 90224199, 5, 102400000, 5, 115856201, 5,
130691232, 5, 147008443, 5, 164916224, 5, 184528125, 5, 205962976, 5, 229345007, 5, 254803968, 5,
282475249, 5, 312500000, 5, 345025251, 5, 380204032, 5, 418195493, 5, 459165024, 5, 503284375, 5,
550731776, 5, 601692057, 5, 656356768, 5, 714924299, 5, 777600000, 5, 844596301, 5, 916132832, 5,
}

var decMaxPow64 = [...]uint64{
0, 0, 0, 0,
9223372036854775808, 63, 4052555153018976267, 39, 4611686018427387904, 31, 7450580596923828125, 27, 9983543956220149760, 25,
8922003266371364727, 23, 9223372036854775808, 21, 1350851717672992089, 19, 10000000000000000000, 19, 8667208279016151025, 20,
8176589207175692288, 18, 8650415919381337933, 17, 2177953337809371136, 16, 6568408355712890625, 16, 1152921504606846976, 15,
2862423051509815793, 15, 6746640616477458432, 15, 799006685782884121, 14, 1638400000000000000, 14, 3243919932521508681, 14,
7752859499445190656, 15, 504036361936467383, 13, 6795192965888212992, 15, 1490116119384765625, 13, 9169742482168496128, 14,
4052555153018976267, 13, 6502111422497947648, 13, 353814783205469041, 12, 531441000000000000, 12, 5970802223735490975, 13,
1152921504606846976, 12, 1667889514952984961, 12, 7351326950727229440, 13, 7592253339725112179, 13, 4738381338321616896, 12,
0, 0, 0, 0, 9223372036854775808, 63, 4052555153018976267, 39, 4611686018427387904, 31, 7450580596923828125, 27, 9983543956220149760, 25,
8922003266371364727, 23, 9223372036854775808, 21, 1350851717672992089, 19, 10000000000000000000, 19, 8667208279016151025, 20, 8176589207175692288, 18, 8650415919381337933, 17,
2177953337809371136, 16, 6568408355712890625, 16, 1152921504606846976, 15, 2862423051509815793, 15, 6746640616477458432, 15, 799006685782884121, 14, 1638400000000000000, 14,
3243919932521508681, 14, 7752859499445190656, 15, 504036361936467383, 13, 6795192965888212992, 15, 1490116119384765625, 13, 9169742482168496128, 14, 4052555153018976267, 13,
6502111422497947648, 13, 353814783205469041, 12, 531441000000000000, 12, 5970802223735490975, 13, 1152921504606846976, 12, 1667889514952984961, 12, 7351326950727229440, 13,
7592253339725112179, 13, 4738381338321616896, 12, 6582952005840035281, 12, 9065737908494995456, 12, 317475837322472439, 11, 419430400000000000, 11, 4116746226656634465, 12,
717368321110468608, 11, 3066142649843473169, 12, 1196683881290399744, 11, 1532278301220703125, 11, 1951354384207722496, 11, 5511018666691268545, 12, 3116402981210161152, 11,
7113790643470898241, 12, 4882812500000000000, 11, 6071163615208263051, 11, 7516865509350965248, 11, 9269035929372191597, 11, 210832519264920576, 10, 253295162119140625, 10,
303305489096114176, 10, 2189155819333249577, 11, 6539899926455986176, 11, 511116753300641401, 10, 604661760000000000, 10, 6620429464016735429, 11, 839299365868340224, 10,
}

// decMaxPow returns (b**n, n) such that b**n is the largest power b**n such that (b**n) <= _BD.
// For instance decMaxPow(10) == (1e19 - 1, 19) for 19 decimal digits in a 64bit Word.
// In other words, at most n digits in base b fit into a decimal Word.
func decMaxPow(b uint) (p uint, n int) {
i := b / 2
func decMaxPow(b Word) (p Word, n int) {
i := b * 2
if bits.UintSize == 32 {
return uint(decMaxPow32[i]), int(decMaxPow32[i+1])
return Word(decMaxPow32[i]), int(decMaxPow32[i+1])
}
return uint(decMaxPow64[i]), int(decMaxPow64[i+1])
return Word(decMaxPow64[i]), int(decMaxPow64[i+1])
}

// addW adds y to x. The resulting carry c is either 0 or 1.
Expand Down
127 changes: 122 additions & 5 deletions dec_conv.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package decimal
import (
"fmt"
"io"
"math"
)

func (z dec) scan(r io.ByteScanner, base int, fracOk bool) (res dec, b, count int, err error) {
Expand Down Expand Up @@ -63,10 +64,10 @@ func (z dec) scan(r io.ByteScanner, base int, fracOk bool) (res dec, b, count in
// result.
z = z[:0]
b1 := Word(b)
bn, n := decMaxPow(uint(b1)) // at most n+1 digits in base b1 fit into Word
di := Word(0) // 0 <= di < b1**i < bn
i := 0 // 0 <= i < n
dp := -1 // position of decimal point
bn, n := decMaxPow(b1) // at most n+1 digits in base b1 fit into Word
di := Word(0) // 0 <= di < b1**i < bn
i := 0 // 0 <= i < n
dp := -1 // position of decimal point
for err == nil {
if ch == '.' && fracOk {
fracOk = false
Expand Down Expand Up @@ -111,7 +112,10 @@ func (z dec) scan(r io.ByteScanner, base int, fracOk bool) (res dec, b, count in
// if di is "full", add it to the result
if i == n {
if bn == _BD {
z = z.shl(z, _WD)
// shift left by _WD digits
t := z.make(len(z) + 1)
copy(t[1:], z)
z = t
z[0] = di
} else {
z = z.mulAddWW(z, Word(bn), di)
Expand Down Expand Up @@ -157,3 +161,116 @@ func (z dec) scan(r io.ByteScanner, base int, fracOk bool) (res dec, b, count in

return
}

// utoa converts x to an ASCII representation in the given base;
// base must be between 2 and MaxBase, inclusive.
func (x dec) utoa(base int) []byte {
return x.itoa(false, base)
}

// itoa is like utoa but it prepends a '-' if neg && x != 0.
func (x dec) itoa(neg bool, base int) []byte {
if base < 2 || base > MaxBase {
panic("invalid base")
}

// x == 0
if len(x) == 0 {
return []byte("0")
}
// len(x) > 0

// allocate buffer for conversion
i := int(float64(x.digits())/math.Log10(float64(base))) + 1 // off by 1 at most
if neg {
i++
}
s := make([]byte, i)

b := Word(base)
bb, ndigits := decMaxPow(b)

// construct table of successive squares of bb*leafSize to use in subdivisions
// result (table != nil) <=> (len(x) > leafSize > 0)
table := divisors(len(x), b, ndigits, bb)

// preserve x, create local copy for use by convertWords
q := dec(nil).set(x)

// convert q to string s in base b
q.convertWords(s, b, ndigits, bb, table)

// strip leading zeros
// (x != 0; thus s must contain at least one non-zero digit
// and the loop will terminate)
i = 0
for s[i] == '0' {
i++
}

if neg {
i--
s[i] = '-'
}

return s[i:]
}

type divisor struct{}

// TODO(db47h): implement recursive algorithm
func (q dec) convertWords(s []byte, b Word, ndigits int, bb Word, table []decDivisor) {
// split larger blocks recursively
if table != nil {
panic("not implemented")
}

// having split any large blocks now process the remaining (small) block iteratively
i := len(s)
var r Word
if b == 10 {
// hard-coding for 10 here speeds this up by 1.25x (allows for / and % by constants)
for len(q) > 0 {
// extract least significant, base bb "digit"
r = q[0]
q = q.shr(q, _WD)
for j := 0; j < ndigits && i > 0; j++ {
i--
// avoid % computation since r%10 == r - int(r/10)*10;
// this appears to be faster for BenchmarkString10000Base10
// and smaller strings (but a bit slower for larger ones)
t := r / 10
s[i] = '0' + byte(r-t*10)
r = t
}
}
} else {
for len(q) > 0 {
// extract least significant, base bb "digit"
q, r = q.divW(q, bb)
for j := 0; j < ndigits && i > 0; j++ {
i--
s[i] = digits[r%b]
r /= b
}
}
}

// prepend high-order zeros
for i > 0 { // while need more leading zeros
i--
s[i] = '0'
}
}

var decLeafSize int = 8 // number of Word-size binary values treat as a monolithic block

type decDivisor struct {
bbb dec // divisor
nbits int // bit length of divisor (discounting leading zeros) ~= log2(bbb)
ndigits int // digit length of divisor in terms of output base digits
}

func divisors(m int, b Word, ndigits int, bb Word) []decDivisor {
return nil
}
Loading

0 comments on commit 9fea708

Please sign in to comment.