From 9fea708bc21f2a488aef1267b8928806f1936e4d Mon Sep 17 00:00:00 2001 From: Denis Bernard Date: Tue, 5 May 2020 02:47:33 +0200 Subject: [PATCH] dec: fix shl/shr, add string conversion to/from dec The loops in shr/shl were reversed. --- dec.go | 58 +++++- dec_arith.go | 84 +++++---- dec_conv.go | 127 +++++++++++++- dec_conv_test.go | 448 +++++++++++++++++++++++++++++++++++++++++++++++ dec_test.go | 3 +- decimal.go | 6 +- decimal_test.go | 4 +- stdlib_test.go | 9 + 8 files changed, 695 insertions(+), 44 deletions(-) create mode 100644 dec_conv_test.go create mode 100644 stdlib_test.go diff --git a/dec.go b/dec.go index ca6b7eb..b1d9033 100644 --- a/dec.go +++ b/dec.go @@ -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 @@ -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 } @@ -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) @@ -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 { diff --git a/dec_arith.go b/dec_arith.go index 53087de..3c37bd2 100644 --- a/dec_arith.go +++ b/dec_arith.go @@ -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 } @@ -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. diff --git a/dec_conv.go b/dec_conv.go index 323bd9b..fe3c417 100644 --- a/dec_conv.go +++ b/dec_conv.go @@ -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) { @@ -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 @@ -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) @@ -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 +} diff --git a/dec_conv_test.go b/dec_conv_test.go new file mode 100644 index 0000000..6790c46 --- /dev/null +++ b/dec_conv_test.go @@ -0,0 +1,448 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package decimal + +import ( + "io" + "math" + "strings" + "testing" +) + +func dtoa(x dec, base int) []byte { + // special cases + switch { + case base < 2: + panic("illegal base") + case len(x) == 0: + return []byte("0") + } + + // allocate buffer for conversion + i := int(float64(x.digits())/math.Log10(float64(base))) + 1 // +1: round up + s := make([]byte, i) + + // don't destroy x + q := dec(nil).set(x) + + // convert + for len(q) > 0 { + i-- + var r Word + q, r = q.divW(q, Word(base)) + s[i] = digits[r] + } + + return s[i:] +} + +var decStrTests = []struct { + x dec // dec value to be converted + b int // conversion base + s string // expected result +}{ + {nil, 2, "0"}, + {dec{1}, 2, "1"}, + {dec{0xc5}, 2, "11000101"}, + {dec{03271}, 8, "3271"}, + {dec{10}, 10, "10"}, + {dec{1234567890}, 10, "1234567890"}, + {dec{0xdeadbeef}, 16, "deadbeef"}, + {dec{0x229be7}, 17, "1a2b3c"}, + {dec{0x309663e6}, 32, "o9cov6"}, + {dec{0x309663e6}, 62, "TakXI"}, +} + +func TestDecString(t *testing.T) { + // test invalid base explicitly + var panicStr string + func() { + defer func() { + panicStr = recover().(string) + }() + decOne.utoa(1) + }() + if panicStr != "invalid base" { + t.Errorf("expected panic for invalid base") + } + + for _, a := range decStrTests { + s := string(a.x.utoa(a.b)) + if s != a.s { + t.Errorf("string%+v\n\tgot s = %s; want %s", a, s, a.s) + } + + x, b, _, err := dec(nil).scan(strings.NewReader(a.s), a.b, false) + if x.cmp(a.x) != 0 { + t.Errorf("scan%+v\n\tgot z = %v; want %v", a, x, a.x) + } + if b != a.b { + t.Errorf("scan%+v\n\tgot b = %d; want %d", a, b, a.b) + } + if err != nil { + t.Errorf("scan%+v\n\tgot error = %s", a, err) + } + } +} + +var decScanTests = []struct { + s string // string to be scanned + base int // input base + frac bool // fraction ok + x dec // expected nat + b int // expected base + count int // expected digit count + err error // expected error + next rune // next character (or 0, if at EOF) +}{ + // invalid: no digits + {"", 0, false, nil, 10, 0, errNoDigits, 0}, + {"_", 0, false, nil, 10, 0, errNoDigits, 0}, + {"?", 0, false, nil, 10, 0, errNoDigits, '?'}, + {"?", 10, false, nil, 10, 0, errNoDigits, '?'}, + {"", 10, false, nil, 10, 0, errNoDigits, 0}, + {"", 36, false, nil, 36, 0, errNoDigits, 0}, + {"", 62, false, nil, 62, 0, errNoDigits, 0}, + {"0b", 0, false, nil, 2, 0, errNoDigits, 0}, + {"0o", 0, false, nil, 8, 0, errNoDigits, 0}, + {"0x", 0, false, nil, 16, 0, errNoDigits, 0}, + {"0x_", 0, false, nil, 16, 0, errNoDigits, 0}, + {"0b2", 0, false, nil, 2, 0, errNoDigits, '2'}, + {"0B2", 0, false, nil, 2, 0, errNoDigits, '2'}, + {"0o8", 0, false, nil, 8, 0, errNoDigits, '8'}, + {"0O8", 0, false, nil, 8, 0, errNoDigits, '8'}, + {"0xg", 0, false, nil, 16, 0, errNoDigits, 'g'}, + {"0Xg", 0, false, nil, 16, 0, errNoDigits, 'g'}, + {"345", 2, false, nil, 2, 0, errNoDigits, '3'}, + + // invalid: incorrect use of decimal point + {"._", 0, true, nil, 10, 0, errNoDigits, 0}, + {".0", 0, false, nil, 10, 0, errNoDigits, '.'}, + {".0", 10, false, nil, 10, 0, errNoDigits, '.'}, + {".", 0, true, nil, 10, 0, errNoDigits, 0}, + {"0x.", 0, true, nil, 16, 0, errNoDigits, 0}, + {"0x.g", 0, true, nil, 16, 0, errNoDigits, 'g'}, + {"0x.0", 0, false, nil, 16, 0, errNoDigits, '.'}, + + // invalid: incorrect use of separators + {"_0", 0, false, nil, 10, 1, errInvalSep, 0}, + {"0_", 0, false, nil, 10, 1, errInvalSep, 0}, + {"0__0", 0, false, nil, 8, 1, errInvalSep, 0}, + {"0x___0", 0, false, nil, 16, 1, errInvalSep, 0}, + {"0_x", 0, false, nil, 10, 1, errInvalSep, 'x'}, + {"0_8", 0, false, nil, 10, 1, errInvalSep, '8'}, + {"123_.", 0, true, dec{123}, 10, 0, errInvalSep, 0}, + {"._123", 0, true, dec{123}, 10, -3, errInvalSep, 0}, + {"0b__1000", 0, false, dec{0x8}, 2, 4, errInvalSep, 0}, + {"0o60___0", 0, false, dec{0600}, 8, 3, errInvalSep, 0}, + {"0466_", 0, false, dec{0466}, 8, 3, errInvalSep, 0}, + {"01234567_8", 0, false, dec{01234567}, 8, 7, errInvalSep, '8'}, + {"1_.", 0, true, dec{1}, 10, 0, errInvalSep, 0}, + {"0._1", 0, true, dec{1}, 10, -1, errInvalSep, 0}, + {"2.7_", 0, true, dec{27}, 10, -1, errInvalSep, 0}, + {"0x1.0_", 0, true, dec{0x10}, 16, -1, errInvalSep, 0}, + + // valid: separators are not accepted for base != 0 + {"0_", 10, false, nil, 10, 1, nil, '_'}, + {"1__0", 10, false, dec{1}, 10, 1, nil, '_'}, + {"0__8", 10, false, nil, 10, 1, nil, '_'}, + {"xy_z_", 36, false, dec{33*36 + 34}, 36, 2, nil, '_'}, + + // valid, no decimal point + {"0", 0, false, nil, 10, 1, nil, 0}, + {"0", 36, false, nil, 36, 1, nil, 0}, + {"0", 62, false, nil, 62, 1, nil, 0}, + {"1", 0, false, dec{1}, 10, 1, nil, 0}, + {"1", 10, false, dec{1}, 10, 1, nil, 0}, + {"0 ", 0, false, nil, 10, 1, nil, ' '}, + {"00 ", 0, false, nil, 8, 1, nil, ' '}, // octal 0 + {"0b1", 0, false, dec{1}, 2, 1, nil, 0}, + {"0B11000101", 0, false, dec{0xc5}, 2, 8, nil, 0}, + {"0B110001012", 0, false, dec{0xc5}, 2, 8, nil, '2'}, + {"07", 0, false, dec{7}, 8, 1, nil, 0}, + {"08", 0, false, nil, 10, 1, nil, '8'}, + {"08", 10, false, dec{8}, 10, 2, nil, 0}, + {"018", 0, false, dec{1}, 8, 1, nil, '8'}, + {"0o7", 0, false, dec{7}, 8, 1, nil, 0}, + {"0o18", 0, false, dec{1}, 8, 1, nil, '8'}, + {"0O17", 0, false, dec{017}, 8, 2, nil, 0}, + {"03271", 0, false, dec{03271}, 8, 4, nil, 0}, + {"10ab", 0, false, dec{10}, 10, 2, nil, 'a'}, + {"1234567890", 0, false, dec{1234567890}, 10, 10, nil, 0}, + {"A", 36, false, dec{10}, 36, 1, nil, 0}, + {"A", 37, false, dec{36}, 37, 1, nil, 0}, + {"xyz", 36, false, dec{(33*36+34)*36 + 35}, 36, 3, nil, 0}, + {"XYZ?", 36, false, dec{(33*36+34)*36 + 35}, 36, 3, nil, '?'}, + {"XYZ?", 62, false, dec{(59*62+60)*62 + 61}, 62, 3, nil, '?'}, + {"0x", 16, false, nil, 16, 1, nil, 'x'}, + {"0xdeadbeef", 0, false, dec{0xdeadbeef}, 16, 8, nil, 0}, + {"0XDEADBEEF", 0, false, dec{0xdeadbeef}, 16, 8, nil, 0}, + + // valid, with decimal point + {"0.", 0, false, nil, 10, 1, nil, '.'}, + {"0.", 10, true, nil, 10, 0, nil, 0}, + {"0.1.2", 10, true, dec{1}, 10, -1, nil, '.'}, + {".000", 10, true, nil, 10, -3, nil, 0}, + {"12.3", 10, true, dec{123}, 10, -1, nil, 0}, + {"012.345", 10, true, dec{12345}, 10, -3, nil, 0}, + {"0.1", 0, true, dec{1}, 10, -1, nil, 0}, + {"0.1", 2, true, dec{1}, 2, -1, nil, 0}, + {"0.12", 2, true, dec{1}, 2, -1, nil, '2'}, + {"0b0.1", 0, true, dec{1}, 2, -1, nil, 0}, + {"0B0.12", 0, true, dec{1}, 2, -1, nil, '2'}, + {"0o0.7", 0, true, dec{7}, 8, -1, nil, 0}, + {"0O0.78", 0, true, dec{7}, 8, -1, nil, '8'}, + {"0xdead.beef", 0, true, dec{0xdeadbeef}, 16, -4, nil, 0}, + + // valid, with separators + {"1_000", 0, false, dec{1000}, 10, 4, nil, 0}, + {"0_466", 0, false, dec{0466}, 8, 3, nil, 0}, + {"0o_600", 0, false, dec{0600}, 8, 3, nil, 0}, + {"0x_f0_0d", 0, false, dec{0xf00d}, 16, 4, nil, 0}, + {"0b1000_0001", 0, false, dec{0x81}, 2, 8, nil, 0}, + {"1_000.000_1", 0, true, dec{10000001}, 10, -4, nil, 0}, + {"0x_f00d.1e", 0, true, dec{0xf00d1e}, 16, -2, nil, 0}, + {"0x_f00d.1E2", 0, true, dec{0xf00d1e2}, 16, -3, nil, 0}, + {"0x_f00d.1eg", 0, true, dec{0xf00d1e}, 16, -2, nil, 'g'}, +} + +func TestDecScanBase(t *testing.T) { + for _, a := range decScanTests { + r := strings.NewReader(a.s) + x, b, count, err := dec(nil).scan(r, a.base, a.frac) + if err != a.err { + t.Errorf("scan%+v\n\tgot error = %v; want %v", a, err, a.err) + } + if x.cmp(a.x) != 0 { + t.Errorf("scan%+v\n\tgot z = %v; want %v", a, x, a.x) + } + if b != a.b { + t.Errorf("scan%+v\n\tgot b = %d; want %d", a, b, a.base) + } + if count != a.count { + t.Errorf("scan%+v\n\tgot count = %d; want %d", a, count, a.count) + } + next, _, err := r.ReadRune() + if err == io.EOF { + next = 0 + err = nil + } + if err == nil && next != a.next { + t.Errorf("scan%+v\n\tgot next = %q; want %q", a, next, a.next) + } + } +} + +var pi = "3" + + "14159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651" + + "32823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461" + + "28475648233786783165271201909145648566923460348610454326648213393607260249141273724587006606315588174881520920" + + "96282925409171536436789259036001133053054882046652138414695194151160943305727036575959195309218611738193261179" + + "31051185480744623799627495673518857527248912279381830119491298336733624406566430860213949463952247371907021798" + + "60943702770539217176293176752384674818467669405132000568127145263560827785771342757789609173637178721468440901" + + "22495343014654958537105079227968925892354201995611212902196086403441815981362977477130996051870721134999999837" + + "29780499510597317328160963185950244594553469083026425223082533446850352619311881710100031378387528865875332083" + + "81420617177669147303598253490428755468731159562863882353787593751957781857780532171226806613001927876611195909" + + "21642019893809525720106548586327886593615338182796823030195203530185296899577362259941389124972177528347913151" + + "55748572424541506959508295331168617278558890750983817546374649393192550604009277016711390098488240128583616035" + + "63707660104710181942955596198946767837449448255379774726847104047534646208046684259069491293313677028989152104" + + "75216205696602405803815019351125338243003558764024749647326391419927260426992279678235478163600934172164121992" + + "45863150302861829745557067498385054945885869269956909272107975093029553211653449872027559602364806654991198818" + + "34797753566369807426542527862551818417574672890977772793800081647060016145249192173217214772350141441973568548" + + "16136115735255213347574184946843852332390739414333454776241686251898356948556209921922218427255025425688767179" + + "04946016534668049886272327917860857843838279679766814541009538837863609506800642251252051173929848960841284886" + + "26945604241965285022210661186306744278622039194945047123713786960956364371917287467764657573962413890865832645" + + "99581339047802759009946576407895126946839835259570982582262052248940772671947826848260147699090264013639443745" + + "53050682034962524517493996514314298091906592509372216964615157098583874105978859597729754989301617539284681382" + + "68683868942774155991855925245953959431049972524680845987273644695848653836736222626099124608051243884390451244" + + "13654976278079771569143599770012961608944169486855584840635342207222582848864815845602850601684273945226746767" + + "88952521385225499546667278239864565961163548862305774564980355936345681743241125150760694794510965960940252288" + + "79710893145669136867228748940560101503308617928680920874760917824938589009714909675985261365549781893129784821" + + "68299894872265880485756401427047755513237964145152374623436454285844479526586782105114135473573952311342716610" + + "21359695362314429524849371871101457654035902799344037420073105785390621983874478084784896833214457138687519435" + + "06430218453191048481005370614680674919278191197939952061419663428754440643745123718192179998391015919561814675" + + "14269123974894090718649423196156794520809514655022523160388193014209376213785595663893778708303906979207734672" + + "21825625996615014215030680384477345492026054146659252014974428507325186660021324340881907104863317346496514539" + + "05796268561005508106658796998163574736384052571459102897064140110971206280439039759515677157700420337869936007" + + "23055876317635942187312514712053292819182618612586732157919841484882916447060957527069572209175671167229109816" + + "90915280173506712748583222871835209353965725121083579151369882091444210067510334671103141267111369908658516398" + + "31501970165151168517143765761835155650884909989859982387345528331635507647918535893226185489632132933089857064" + + "20467525907091548141654985946163718027098199430992448895757128289059232332609729971208443357326548938239119325" + + "97463667305836041428138830320382490375898524374417029132765618093773444030707469211201913020330380197621101100" + + "44929321516084244485963766983895228684783123552658213144957685726243344189303968642624341077322697802807318915" + + "44110104468232527162010526522721116603966655730925471105578537634668206531098965269186205647693125705863566201" + + "85581007293606598764861179104533488503461136576867532494416680396265797877185560845529654126654085306143444318" + + "58676975145661406800700237877659134401712749470420562230538994561314071127000407854733269939081454664645880797" + + "27082668306343285878569830523580893306575740679545716377525420211495576158140025012622859413021647155097925923" + + "09907965473761255176567513575178296664547791745011299614890304639947132962107340437518957359614589019389713111" + + "79042978285647503203198691514028708085990480109412147221317947647772622414254854540332157185306142288137585043" + + "06332175182979866223717215916077166925474873898665494945011465406284336639379003976926567214638530673609657120" + + "91807638327166416274888800786925602902284721040317211860820419000422966171196377921337575114959501566049631862" + + "94726547364252308177036751590673502350728354056704038674351362222477158915049530984448933309634087807693259939" + + "78054193414473774418426312986080998886874132604721569516239658645730216315981931951673538129741677294786724229" + + "24654366800980676928238280689964004824354037014163149658979409243237896907069779422362508221688957383798623001" + + "59377647165122893578601588161755782973523344604281512627203734314653197777416031990665541876397929334419521541" + + "34189948544473456738316249934191318148092777710386387734317720754565453220777092120190516609628049092636019759" + + "88281613323166636528619326686336062735676303544776280350450777235547105859548702790814356240145171806246436267" + + "94561275318134078330336254232783944975382437205835311477119926063813346776879695970309833913077109870408591337" + +// Test case for BenchmarkScanPi. +func TestDecScanPi(t *testing.T) { + var x dec + z, _, _, err := x.scan(strings.NewReader(pi), 10, false) + if err != nil { + t.Errorf("scanning pi: %s", err) + } + if s := string(z.utoa(10)); s != pi { + t.Errorf("scanning pi: got %s", s) + } +} + +func TestDecScanPiParallel(t *testing.T) { + const n = 2 + c := make(chan int) + for i := 0; i < n; i++ { + go func() { + TestDecScanPi(t) + c <- 0 + }() + } + for i := 0; i < n; i++ { + <-c + } +} + +func BenchmarkDecScanPi(b *testing.B) { + for i := 0; i < b.N; i++ { + var x dec + x.scan(strings.NewReader(pi), 10, false) + } +} + +func BenchmarkDecStringPiParallel(b *testing.B) { + var x dec + x, _, _, _ = x.scan(strings.NewReader(pi), 0, false) + if string(x.utoa(10)) != pi { + panic("benchmark incorrect: conversion failed") + } + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + x.utoa(10) + } + }) +} + +// func BenchmarkScan(b *testing.B) { +// const x = 10 +// for _, base := range []int{2, 8, 10, 16} { +// for _, y := range []Word{10, 100, 1000, 10000, 100000} { +// if isRaceBuilder && y > 1000 { +// continue +// } +// b.Run(fmt.Sprintf("%d/Base%d", y, base), func(b *testing.B) { +// b.StopTimer() +// var z nat +// z = z.expWW(x, y) + +// s := z.utoa(base) +// if t := itoa(z, base); !bytes.Equal(s, t) { +// b.Fatalf("scanning: got %s; want %s", s, t) +// } +// b.StartTimer() + +// for i := 0; i < b.N; i++ { +// z.scan(bytes.NewReader(s), base, false) +// } +// }) +// } +// } +// } + +// func BenchmarkString(b *testing.B) { +// const x = 10 +// for _, base := range []int{2, 8, 10, 16} { +// for _, y := range []Word{10, 100, 1000, 10000, 100000} { +// if isRaceBuilder && y > 1000 { +// continue +// } +// b.Run(fmt.Sprintf("%d/Base%d", y, base), func(b *testing.B) { +// b.StopTimer() +// var z dec +// z = z.expWW(x, y) +// z.utoa(base) // warm divisor cache +// b.StartTimer() + +// for i := 0; i < b.N; i++ { +// _ = z.utoa(base) +// } +// }) +// } +// } +// } + +// func BenchmarkLeafSize(b *testing.B) { +// for n := 0; n <= 16; n++ { +// b.Run(fmt.Sprint(n), func(b *testing.B) { LeafSizeHelper(b, 10, n) }) +// } +// // Try some large lengths +// for _, n := range []int{32, 64} { +// b.Run(fmt.Sprint(n), func(b *testing.B) { LeafSizeHelper(b, 10, n) }) +// } +// } + +// func LeafSizeHelper(b *testing.B, base, size int) { +// b.StopTimer() +// originalLeafSize := leafSize +// resetTable(cacheBase10.table[:]) +// leafSize = size +// b.StartTimer() + +// for d := 1; d <= 10000; d *= 10 { +// b.StopTimer() +// var z nat +// z = z.expWW(Word(base), Word(d)) // build target number +// _ = z.utoa(base) // warm divisor cache +// b.StartTimer() + +// for i := 0; i < b.N; i++ { +// _ = z.utoa(base) +// } +// } + +// b.StopTimer() +// resetTable(cacheBase10.table[:]) +// leafSize = originalLeafSize +// b.StartTimer() +// } + +// func resetTable(table []divisor) { +// if table != nil && table[0].bbb != nil { +// for i := 0; i < len(table); i++ { +// table[i].bbb = nil +// table[i].nbits = 0 +// table[i].ndigits = 0 +// } +// } +// } + +// func TestStringPowers(t *testing.T) { +// var p Word +// for b := 2; b <= 16; b++ { +// for p = 0; p <= 512; p++ { +// if testing.Short() && p > 10 { +// break +// } +// x := nat(nil).expWW(Word(b), p) +// xs := x.utoa(b) +// xs2 := itoa(x, b) +// if !bytes.Equal(xs, xs2) { +// t.Errorf("failed at %d ** %d in base %d: %s != %s", b, p, b, xs, xs2) +// } +// } +// if b >= 3 && testing.Short() { +// break +// } +// } +// } diff --git a/dec_test.go b/dec_test.go index 5e8d30d..9a2c160 100644 --- a/dec_test.go +++ b/dec_test.go @@ -50,13 +50,12 @@ func TestDec_digit(t *testing.T) { } var ( - benchD dec benchU uint ) func BenchmarkDec_Digits(b *testing.B) { rand.Seed(0xdeadbeefbadf00d) - d := dec{}.make(10000) + d := dec(nil).make(10000) for i := range d { d[i] = Word(rand.Uint64()) % _BD } diff --git a/decimal.go b/decimal.go index 5fc6c9d..0998a5a 100644 --- a/decimal.go +++ b/decimal.go @@ -173,15 +173,15 @@ func (z *Decimal) SetInf(signbit bool) *Decimal { return z } -const ln2_10 = math.Ln10 / math.Ln2 +const log2_10 = math.Ln10 / math.Ln2 // SetInt sets z to the (possibly rounded) value of x and returns z. // If z's precision is 0, it is changed to the larger of x.BitLen() // or 64 (and rounding will have no effect). func (z *Decimal) SetInt(x *big.Int) *Decimal { bits := uint32(x.BitLen()) - // estimate precision. May overshoot actual precision by 1. - prec := uint32(float64(bits)/ln2_10) + 1 + prec := uint32(float64(bits)/log2_10) + 1 // off by 1 at most + // TODO(db47h): adjust precision if needed if z.prec == 0 { z.prec = umax32(prec, _WD) } diff --git a/decimal_test.go b/decimal_test.go index 53e0377..f9cf90b 100644 --- a/decimal_test.go +++ b/decimal_test.go @@ -45,7 +45,7 @@ func TestDnorm(t *testing.T) { } t.Fatalf("dec{%v, %v}).norm() returned dec{} for word %d", l, h, w) } - dd := dec{}.set(d) + dd := dec(nil).set(d) s := dnorm(dd) // d should now have a single element with e shifted left ew := w * pow10(_WD-decDigits(w)) @@ -94,7 +94,7 @@ func TestDecimal_SetString(t *testing.T) { func BenchmarkDnorm(b *testing.B) { rand.Seed(0xdeadbeefbadf00d) - d := dec{}.make(1000) + d := dec(nil).make(1000) for i := range d { d[i] = Word(rand.Uint64()) % _BD } diff --git a/stdlib_test.go b/stdlib_test.go new file mode 100644 index 0000000..5f26805 --- /dev/null +++ b/stdlib_test.go @@ -0,0 +1,9 @@ +package decimal + +import "testing" + +func TestMaxBase(t *testing.T) { + if MaxBase != len(digits) { + t.Fatalf("%d != %d", MaxBase, len(digits)) + } +}