-
Notifications
You must be signed in to change notification settings - Fork 37
/
table.go
137 lines (120 loc) · 3.76 KB
/
table.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
// Copyright 2016 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package apd
// digitsLookupTable is used to map binary digit counts to their corresponding
// decimal border values. The map relies on the proof that (without leading zeros)
// for any given number of binary digits r, such that the number represented is
// between 2^r and 2^(r+1)-1, there are only two possible decimal digit counts
// k and k+1 that the binary r digits could be representing.
//
// Using this proof, for a given digit count, the map will return the lower number
// of decimal digits (k) the binary digit count could represent, along with the
// value of the border between the two decimal digit counts (10^k).
const digitsTableSize = 128
var digitsLookupTable [digitsTableSize + 1]tableVal
type tableVal struct {
digits int64
border BigInt
nborder BigInt
}
func init() {
curVal := NewBigInt(1)
curExp := new(BigInt)
for i := 1; i <= digitsTableSize; i++ {
if i > 1 {
curVal.Lsh(curVal, 1)
}
elem := &digitsLookupTable[i]
elem.digits = int64(len(curVal.String()))
elem.border.SetInt64(10)
curExp.SetInt64(elem.digits)
elem.border.Exp(&elem.border, curExp, nil)
elem.nborder.Neg(&elem.border)
}
}
// NumDigits returns the number of decimal digits of d.Coeff.
//gcassert:inline
func (d *Decimal) NumDigits() int64 {
return NumDigits(&d.Coeff)
}
// NumDigits returns the number of decimal digits of b.
func NumDigits(b *BigInt) int64 {
bl := b.BitLen()
if bl == 0 {
return 1
}
if bl <= digitsTableSize {
val := &digitsLookupTable[bl]
// In general, we either have val.digits or val.digits+1 digits and we have
// to compare with the border value. But that's not true for all values of
// bl: in particular, if bl+1 maps to the same number of digits, then we
// know for sure we have val.digits and we can skip the comparison.
// This is the case for about 2 out of 3 values.
if bl < digitsTableSize && digitsLookupTable[bl+1].digits == val.digits {
return val.digits
}
switch b.Sign() {
case 1:
if b.Cmp(&val.border) < 0 {
return val.digits
}
case -1:
if b.Cmp(&val.nborder) > 0 {
return val.digits
}
}
return val.digits + 1
}
n := int64(float64(bl) / digitsToBitsRatio)
var tmp BigInt
e := tableExp10(n, &tmp)
var a *BigInt
if b.Sign() < 0 {
var tmpA BigInt
a := &tmpA
a.Abs(b)
} else {
a = b
}
if a.Cmp(e) >= 0 {
n++
}
return n
}
// powerTenTableSize is the magnitude of the maximum power of 10 exponent that
// is stored in the pow10LookupTable. For instance, if the powerTenTableSize
// if 3, then the lookup table will store power of 10 values from 10^0 to
// 10^3 inclusive.
const powerTenTableSize = 128
var pow10LookupTable [powerTenTableSize + 1]BigInt
func init() {
for i := int64(0); i <= powerTenTableSize; i++ {
setBigWithPow(&pow10LookupTable[i], i)
}
}
func setBigWithPow(res *BigInt, pow int64) {
var tmp BigInt
tmp.SetInt64(pow)
res.Exp(bigTen, &tmp, nil)
}
// tableExp10 returns 10^x for x >= 0, looked up from a table when
// possible. This returned value must not be mutated. tmp is used as an
// intermediate variable and must not be nil.
func tableExp10(x int64, tmp *BigInt) *BigInt {
if x <= powerTenTableSize {
return &pow10LookupTable[x]
}
setBigWithPow(tmp, x)
return tmp
}