Golang implemented sidechain for Bytom
修订版 | 4c90195eed7ddee19d646520f06fd6079a322895 (tree) |
---|---|
时间 | 2020-01-22 15:31:09 |
作者 | shenao78 <shenao.78@163....> |
Commiter | shenao78 |
fix decimal
@@ -10,6 +10,7 @@ import ( | ||
10 | 10 | "github.com/bytom/vapor/consensus/segwit" |
11 | 11 | "github.com/bytom/vapor/errors" |
12 | 12 | vprMath "github.com/bytom/vapor/math" |
13 | + "github.com/bytom/vapor/math/decimal" | |
13 | 14 | "github.com/bytom/vapor/protocol/bc" |
14 | 15 | "github.com/bytom/vapor/protocol/bc/types" |
15 | 16 | "github.com/bytom/vapor/protocol/vm" |
@@ -240,20 +241,18 @@ func IsMatched(orders []*common.Order) bool { | ||
240 | 241 | return false |
241 | 242 | } |
242 | 243 | |
243 | - rate := orderRatio(sortedOrders[0]) | |
244 | - oppositeRate := big.NewFloat(0).SetInt64(1) | |
244 | + rate := decimal.New(1, 0).Div(orderRatio(sortedOrders[0])) | |
245 | + oppositeRate := decimal.New(1, 0) | |
245 | 246 | for i := 1; i < len(sortedOrders); i++ { |
246 | - oppositeRate.Mul(oppositeRate, orderRatio(sortedOrders[i])) | |
247 | + oppositeRate = oppositeRate.Mul(orderRatio(sortedOrders[i])) | |
247 | 248 | } |
248 | - | |
249 | - one := big.NewFloat(0).SetInt64(1) | |
250 | - return one.Quo(one, rate).Cmp(oppositeRate) >= 0 | |
249 | + return rate.Cmp(oppositeRate) >= 0 | |
251 | 250 | } |
252 | 251 | |
253 | -func orderRatio(order *common.Order) *big.Float { | |
254 | - ratio := big.NewFloat(0).SetInt64(order.RatioNumerator) | |
255 | - ratio.Quo(ratio, big.NewFloat(0).SetInt64(order.RatioDenominator)) | |
256 | - return ratio | |
252 | +func orderRatio(order *common.Order) *decimal.Decimal { | |
253 | + numerator := decimal.New(order.RatioNumerator, 0) | |
254 | + denominator := decimal.New(order.RatioDenominator, 0) | |
255 | + return numerator.Div(denominator) | |
257 | 256 | } |
258 | 257 | |
259 | 258 | func setMatchTxArguments(txInput *types.TxInput, isPartialTrade bool, position int, receiveAmounts uint64) { |
@@ -256,3 +256,163 @@ func TestValidateTradePairs(t *testing.T) { | ||
256 | 256 | } |
257 | 257 | } |
258 | 258 | } |
259 | + | |
260 | +func TestIsMatched(t *testing.T) { | |
261 | + cases := []struct { | |
262 | + desc string | |
263 | + orders []*common.Order | |
264 | + wantMatched bool | |
265 | + }{ | |
266 | + { | |
267 | + desc: "ratio is equals", | |
268 | + orders: []*common.Order{ | |
269 | + { | |
270 | + FromAssetID: &mock.BTC, | |
271 | + ToAssetID: &mock.ETH, | |
272 | + RatioNumerator: 6250, | |
273 | + RatioDenominator: 3, | |
274 | + }, | |
275 | + { | |
276 | + FromAssetID: &mock.ETH, | |
277 | + ToAssetID: &mock.BTC, | |
278 | + RatioNumerator: 3, | |
279 | + RatioDenominator: 6250, | |
280 | + }, | |
281 | + }, | |
282 | + wantMatched: true, | |
283 | + }, | |
284 | + { | |
285 | + desc: "ratio is equals, and numerator and denominator are multiples of the opposite", | |
286 | + orders: []*common.Order{ | |
287 | + { | |
288 | + FromAssetID: &mock.BTC, | |
289 | + ToAssetID: &mock.ETH, | |
290 | + RatioNumerator: 6250, | |
291 | + RatioDenominator: 3, | |
292 | + }, | |
293 | + { | |
294 | + FromAssetID: &mock.ETH, | |
295 | + ToAssetID: &mock.BTC, | |
296 | + RatioNumerator: 9, | |
297 | + RatioDenominator: 18750, | |
298 | + }, | |
299 | + }, | |
300 | + wantMatched: true, | |
301 | + }, | |
302 | + { | |
303 | + desc: "matched with a slight diff", | |
304 | + orders: []*common.Order{ | |
305 | + { | |
306 | + FromAssetID: &mock.BTC, | |
307 | + ToAssetID: &mock.ETH, | |
308 | + RatioNumerator: 62500000000000000, | |
309 | + RatioDenominator: 30000000000001, | |
310 | + }, | |
311 | + { | |
312 | + FromAssetID: &mock.ETH, | |
313 | + ToAssetID: &mock.BTC, | |
314 | + RatioNumerator: 3, | |
315 | + RatioDenominator: 6250, | |
316 | + }, | |
317 | + }, | |
318 | + wantMatched: true, | |
319 | + }, | |
320 | + { | |
321 | + desc: "not matched with a slight diff", | |
322 | + orders: []*common.Order{ | |
323 | + { | |
324 | + FromAssetID: &mock.BTC, | |
325 | + ToAssetID: &mock.ETH, | |
326 | + RatioNumerator: 62500000000000001, | |
327 | + RatioDenominator: 30000000000000, | |
328 | + }, | |
329 | + { | |
330 | + FromAssetID: &mock.ETH, | |
331 | + ToAssetID: &mock.BTC, | |
332 | + RatioNumerator: 3, | |
333 | + RatioDenominator: 6250, | |
334 | + }, | |
335 | + }, | |
336 | + wantMatched: false, | |
337 | + }, | |
338 | + { | |
339 | + desc: "ring matched", | |
340 | + orders: []*common.Order{ | |
341 | + { | |
342 | + FromAssetID: &mock.BTC, | |
343 | + ToAssetID: &mock.ETH, | |
344 | + RatioNumerator: 2000000003, | |
345 | + RatioDenominator: 100000001, | |
346 | + }, | |
347 | + { | |
348 | + FromAssetID: &mock.ETH, | |
349 | + ToAssetID: &mock.EOS, | |
350 | + RatioNumerator: 400000000001, | |
351 | + RatioDenominator: 2000000003, | |
352 | + }, | |
353 | + { | |
354 | + FromAssetID: &mock.EOS, | |
355 | + ToAssetID: &mock.BTC, | |
356 | + RatioNumerator: 100000001, | |
357 | + RatioDenominator: 400000000001, | |
358 | + }, | |
359 | + }, | |
360 | + wantMatched: true, | |
361 | + }, | |
362 | + { | |
363 | + desc: "ring matched with a slight diff", | |
364 | + orders: []*common.Order{ | |
365 | + { | |
366 | + FromAssetID: &mock.BTC, | |
367 | + ToAssetID: &mock.ETH, | |
368 | + RatioNumerator: 2000000003, | |
369 | + RatioDenominator: 100000001, | |
370 | + }, | |
371 | + { | |
372 | + FromAssetID: &mock.ETH, | |
373 | + ToAssetID: &mock.EOS, | |
374 | + RatioNumerator: 400000000001, | |
375 | + RatioDenominator: 2000000003, | |
376 | + }, | |
377 | + { | |
378 | + FromAssetID: &mock.EOS, | |
379 | + ToAssetID: &mock.BTC, | |
380 | + RatioNumerator: 100000000, | |
381 | + RatioDenominator: 400000000001, | |
382 | + }, | |
383 | + }, | |
384 | + wantMatched: true, | |
385 | + }, | |
386 | + { | |
387 | + desc: "ring fail matched with a slight diff", | |
388 | + orders: []*common.Order{ | |
389 | + { | |
390 | + FromAssetID: &mock.BTC, | |
391 | + ToAssetID: &mock.ETH, | |
392 | + RatioNumerator: 2000000003, | |
393 | + RatioDenominator: 100000001, | |
394 | + }, | |
395 | + { | |
396 | + FromAssetID: &mock.ETH, | |
397 | + ToAssetID: &mock.EOS, | |
398 | + RatioNumerator: 400000000001, | |
399 | + RatioDenominator: 2000000003, | |
400 | + }, | |
401 | + { | |
402 | + FromAssetID: &mock.EOS, | |
403 | + ToAssetID: &mock.BTC, | |
404 | + RatioNumerator: 100000002, | |
405 | + RatioDenominator: 400000000001, | |
406 | + }, | |
407 | + }, | |
408 | + wantMatched: false, | |
409 | + }, | |
410 | + } | |
411 | + | |
412 | + for i, c := range cases { | |
413 | + gotMatched := IsMatched(c.orders) | |
414 | + if gotMatched != c.wantMatched { | |
415 | + t.Errorf("#%d(%s) got matched:%v, want matched:%v", i, c.desc, gotMatched, c.wantMatched) | |
416 | + } | |
417 | + } | |
418 | +} |
@@ -0,0 +1,143 @@ | ||
1 | +package decimal | |
2 | + | |
3 | +import ( | |
4 | + "github.com/pingcap/types" | |
5 | + "github.com/sirupsen/logrus" | |
6 | +) | |
7 | + | |
8 | +type Decimal struct { | |
9 | + value *types.MyDecimal | |
10 | +} | |
11 | + | |
12 | +func NewFromString(value string) (*Decimal, error) { | |
13 | + dec := new(types.MyDecimal) | |
14 | + if err := dec.FromString([]byte(value)); err != nil && err != types.ErrTruncated { | |
15 | + return nil, err | |
16 | + } | |
17 | + | |
18 | + return &Decimal{value: dec}, nil | |
19 | +} | |
20 | + | |
21 | +func New(value int64, exp int) *Decimal { | |
22 | + dec := types.NewDecFromInt(value) | |
23 | + if exp < 0 { | |
24 | + pow := types.NewDecFromInt(1) | |
25 | + if err := pow.Shift(-exp); err != nil { | |
26 | + logFatal(err,"decimal shift error") | |
27 | + } | |
28 | + | |
29 | + if err := types.DecimalDiv(dec, pow, dec, 10); err != nil { | |
30 | + logFatal(err,"decimal divide error") | |
31 | + } | |
32 | + } else { | |
33 | + if err := dec.Shift(exp); err != nil { | |
34 | + logFatal(err,"decimal shift error") | |
35 | + } | |
36 | + } | |
37 | + return &Decimal{value: dec} | |
38 | +} | |
39 | + | |
40 | +func (d *Decimal) Abs() *Decimal { | |
41 | + coefficient := New(1, 0) | |
42 | + if d.value.IsNegative() { | |
43 | + coefficient = New(-1, 0) | |
44 | + } | |
45 | + return d.Mul(coefficient) | |
46 | +} | |
47 | + | |
48 | +func (d *Decimal) Add(d2 *Decimal) *Decimal { | |
49 | + result := new(types.MyDecimal) | |
50 | + if err := types.DecimalAdd(d.value, d2.value, result); err != nil { | |
51 | + logFatal(err,"decimal add error") | |
52 | + } | |
53 | + | |
54 | + return &Decimal{value: result} | |
55 | +} | |
56 | + | |
57 | +func (d *Decimal) Sub(d2 *Decimal) *Decimal { | |
58 | + result := new(types.MyDecimal) | |
59 | + if err := types.DecimalSub(d.value, d2.value, result); err != nil { | |
60 | + logFatal(err,"decimal subtract error") | |
61 | + } | |
62 | + | |
63 | + return &Decimal{value: result} | |
64 | +} | |
65 | + | |
66 | +func (d *Decimal) Mul(d2 *Decimal) *Decimal { | |
67 | + result := new(types.MyDecimal) | |
68 | + if err := types.DecimalMul(d.value, d2.value, result); err != nil { | |
69 | + logFatal(err,"decimal multiply error") | |
70 | + } | |
71 | + | |
72 | + return &Decimal{value: result} | |
73 | +} | |
74 | + | |
75 | +func (d *Decimal) Div(d2 *Decimal) *Decimal { | |
76 | + result := new(types.MyDecimal) | |
77 | + if err := types.DecimalDiv(d.value, d2.value, result, 10); err != nil { | |
78 | + logFatal(err,"decimal divide error") | |
79 | + } | |
80 | + | |
81 | + return &Decimal{value: result} | |
82 | +} | |
83 | + | |
84 | +func (d *Decimal) Float64() float64 { | |
85 | + result, err := d.value.ToFloat64() | |
86 | + if err != nil { | |
87 | + logFatal(err,"decimal to float64 error") | |
88 | + } | |
89 | + | |
90 | + return result | |
91 | +} | |
92 | + | |
93 | +func (d *Decimal) Int64() int64 { | |
94 | + result, err := d.value.ToInt() | |
95 | + if err != nil { | |
96 | + logFatal(err,"decimal to int64 error") | |
97 | + } | |
98 | + | |
99 | + return result | |
100 | +} | |
101 | + | |
102 | +func (d *Decimal) Cmp(d2 *Decimal) int { | |
103 | + return d.value.Compare(d2.value) | |
104 | +} | |
105 | + | |
106 | +func (d *Decimal) LessThanOrEqual(d2 *Decimal) bool { | |
107 | + return d.Cmp(d2) <= 0 | |
108 | +} | |
109 | + | |
110 | +func (d *Decimal) GreaterThan(d2 *Decimal) bool { | |
111 | + return d.Cmp(d2) > 0 | |
112 | +} | |
113 | + | |
114 | +func (d *Decimal) String() string { | |
115 | + return d.value.String() | |
116 | +} | |
117 | + | |
118 | +func (d *Decimal) StringRoundFixed(places int) string { | |
119 | + result := new(types.MyDecimal) | |
120 | + if err := d.value.Round(result, places, types.ModeHalfEven); err != nil { | |
121 | + logFatal(err, "decimal string round fixed error") | |
122 | + } | |
123 | + | |
124 | + return result.String() | |
125 | +} | |
126 | + | |
127 | +func (d *Decimal) StringCeilFixed(places int) string { | |
128 | + fixedRate, err := NewFromString(d.StringRoundFixed(places)) | |
129 | + if err != nil { | |
130 | + logFatal(err, "decimal string ceil fixed error") | |
131 | + } | |
132 | + | |
133 | + if fixedRate.Cmp(d) != 0 { | |
134 | + fixedRate = fixedRate.Add(New(1, -places)) | |
135 | + } | |
136 | + return fixedRate.StringRoundFixed(places) | |
137 | +} | |
138 | + | |
139 | +func logFatal(err error, args ...interface{}) { | |
140 | + if err != types.ErrTruncated { | |
141 | + logrus.WithField("err", err).Fatal(args...) | |
142 | + } | |
143 | +} |
@@ -0,0 +1,168 @@ | ||
1 | +package types | |
2 | + | |
3 | +// Copyright 2015 PingCAP, Inc. | |
4 | +// | |
5 | +// Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | +// you may not use this file except in compliance with the License. | |
7 | +// You may obtain a copy of the License at | |
8 | +// | |
9 | +// http://www.apache.org/licenses/LICENSE-2.0 | |
10 | +// | |
11 | +// Unless required by applicable law or agreed to in writing, software | |
12 | +// distributed under the License is distributed on an "AS IS" BASIS, | |
13 | +// See the License for the specific language governing permissions and | |
14 | +// limitations under the License. | |
15 | + | |
16 | +import ( | |
17 | + "math" | |
18 | + "strings" | |
19 | + "unicode" | |
20 | + | |
21 | + "github.com/sirupsen/logrus" | |
22 | +) | |
23 | + | |
24 | +// RoundFloat rounds float val to the nearest integer value with float64 format, like MySQL Round function. | |
25 | +// RoundFloat uses default rounding mode, see https://dev.mysql.com/doc/refman/5.7/en/precision-math-rounding.html | |
26 | +// so rounding use "round half away from zero". | |
27 | +// e.g, 1.5 -> 2, -1.5 -> -2. | |
28 | +func RoundFloat(f float64) float64 { | |
29 | + if math.Abs(f) < 0.5 { | |
30 | + return 0 | |
31 | + } | |
32 | + | |
33 | + return math.Trunc(f + math.Copysign(0.5, f)) | |
34 | +} | |
35 | + | |
36 | +// Round rounds the argument f to dec decimal places. | |
37 | +// dec defaults to 0 if not specified. dec can be negative | |
38 | +// to cause dec digits left of the decimal point of the | |
39 | +// value f to become zero. | |
40 | +func Round(f float64, dec int) float64 { | |
41 | + shift := math.Pow10(dec) | |
42 | + tmp := f * shift | |
43 | + if math.IsInf(tmp, 0) { | |
44 | + return f | |
45 | + } | |
46 | + return RoundFloat(tmp) / shift | |
47 | +} | |
48 | + | |
49 | +// Truncate truncates the argument f to dec decimal places. | |
50 | +// dec defaults to 0 if not specified. dec can be negative | |
51 | +// to cause dec digits left of the decimal point of the | |
52 | +// value f to become zero. | |
53 | +func Truncate(f float64, dec int) float64 { | |
54 | + shift := math.Pow10(dec) | |
55 | + tmp := f * shift | |
56 | + if math.IsInf(tmp, 0) { | |
57 | + return f | |
58 | + } | |
59 | + return math.Trunc(tmp) / shift | |
60 | +} | |
61 | + | |
62 | +func isSpace(c byte) bool { | |
63 | + return c == ' ' || c == '\t' | |
64 | +} | |
65 | + | |
66 | +func isDigit(c byte) bool { | |
67 | + return c >= '0' && c <= '9' | |
68 | +} | |
69 | + | |
70 | +func myMax(a, b int) int { | |
71 | + if a > b { | |
72 | + return a | |
73 | + } | |
74 | + return b | |
75 | +} | |
76 | + | |
77 | +func myMaxInt8(a, b int8) int8 { | |
78 | + if a > b { | |
79 | + return a | |
80 | + } | |
81 | + return b | |
82 | +} | |
83 | + | |
84 | +func myMin(a, b int) int { | |
85 | + if a < b { | |
86 | + return a | |
87 | + } | |
88 | + return b | |
89 | +} | |
90 | + | |
91 | +func myMinInt8(a, b int8) int8 { | |
92 | + if a < b { | |
93 | + return a | |
94 | + } | |
95 | + return b | |
96 | +} | |
97 | + | |
98 | +const ( | |
99 | + maxUint = uint64(math.MaxUint64) | |
100 | + uintCutOff = maxUint/uint64(10) + 1 | |
101 | + intCutOff = uint64(math.MaxInt64) + 1 | |
102 | +) | |
103 | + | |
104 | +// strToInt converts a string to an integer in best effort. | |
105 | +func strToInt(str string) (int64, error) { | |
106 | + str = strings.TrimSpace(str) | |
107 | + if len(str) == 0 { | |
108 | + return 0, ErrTruncated | |
109 | + } | |
110 | + negative := false | |
111 | + i := 0 | |
112 | + if str[i] == '-' { | |
113 | + negative = true | |
114 | + i++ | |
115 | + } else if str[i] == '+' { | |
116 | + i++ | |
117 | + } | |
118 | + | |
119 | + var ( | |
120 | + err error | |
121 | + hasNum = false | |
122 | + ) | |
123 | + r := uint64(0) | |
124 | + for ; i < len(str); i++ { | |
125 | + if !unicode.IsDigit(rune(str[i])) { | |
126 | + err = ErrTruncated | |
127 | + break | |
128 | + } | |
129 | + hasNum = true | |
130 | + if r >= uintCutOff { | |
131 | + r = 0 | |
132 | + err = ErrBadNumber | |
133 | + break | |
134 | + } | |
135 | + r = r * uint64(10) | |
136 | + | |
137 | + r1 := r + uint64(str[i]-'0') | |
138 | + if r1 < r || r1 > maxUint { | |
139 | + r = 0 | |
140 | + err = ErrBadNumber | |
141 | + break | |
142 | + } | |
143 | + r = r1 | |
144 | + } | |
145 | + if !hasNum { | |
146 | + err = ErrTruncated | |
147 | + } | |
148 | + | |
149 | + if !negative && r >= intCutOff { | |
150 | + return math.MaxInt64, ErrBadNumber | |
151 | + } | |
152 | + | |
153 | + if negative && r > intCutOff { | |
154 | + return math.MinInt64, ErrBadNumber | |
155 | + } | |
156 | + | |
157 | + if negative { | |
158 | + r = -r | |
159 | + } | |
160 | + return int64(r), err | |
161 | +} | |
162 | + | |
163 | +// Log logs the error if it is not nil. | |
164 | +func Log(err error) { | |
165 | + if err != nil { | |
166 | + logrus.Error("encountered error", err) | |
167 | + } | |
168 | +} |
@@ -0,0 +1,2332 @@ | ||
1 | +// Copyright 2016 PingCAP, Inc. | |
2 | +// | |
3 | +// Licensed under the Apache License, Version 2.0 (the "License"); | |
4 | +// you may not use this file except in compliance with the License. | |
5 | +// You may obtain a copy of the License at | |
6 | +// | |
7 | +// http://www.apache.org/licenses/LICENSE-2.0 | |
8 | +// | |
9 | +// Unless required by applicable law or agreed to in writing, software | |
10 | +// distributed under the License is distributed on an "AS IS" BASIS, | |
11 | +// See the License for the specific language governing permissions and | |
12 | +// limitations under the License. | |
13 | + | |
14 | +package types | |
15 | + | |
16 | +import ( | |
17 | + "errors" | |
18 | + "math" | |
19 | + "strconv" | |
20 | +) | |
21 | + | |
22 | +// RoundMode is the type for round mode. | |
23 | +type RoundMode int32 | |
24 | + | |
25 | +var ( | |
26 | + // ErrTruncated is returned when data has been truncated during conversion. | |
27 | + ErrTruncated = errors.New("err truncated") | |
28 | + // ErrOverflow is returned when data is out of range for a field type. | |
29 | + ErrOverflow = errors.New("err overflow") | |
30 | + // ErrDivByZero is return when do division by 0. | |
31 | + ErrDivByZero = errors.New("err div by zero") | |
32 | + // ErrBadNumber is return when parsing an invalid binary decimal number. | |
33 | + ErrBadNumber = errors.New("err bad number") | |
34 | +) | |
35 | + | |
36 | +// constant values. | |
37 | +const ( | |
38 | + ten0 = 1 | |
39 | + ten1 = 10 | |
40 | + ten2 = 100 | |
41 | + ten3 = 1000 | |
42 | + ten4 = 10000 | |
43 | + ten5 = 100000 | |
44 | + ten6 = 1000000 | |
45 | + ten7 = 10000000 | |
46 | + ten8 = 100000000 | |
47 | + ten9 = 1000000000 | |
48 | + | |
49 | + maxWordBufLen = 9 // A MyDecimal holds 9 words. | |
50 | + digitsPerWord = 9 // A word holds 9 digits. | |
51 | + wordSize = 4 // A word is 4 bytes int32. | |
52 | + digMask = ten8 | |
53 | + wordBase = ten9 | |
54 | + wordMax = wordBase - 1 | |
55 | + notFixedDec = 31 | |
56 | + | |
57 | + DivFracIncr = 4 | |
58 | + | |
59 | + // ModeHalfEven rounds normally. | |
60 | + ModeHalfEven RoundMode = 5 | |
61 | + // Truncate just truncates the decimal. | |
62 | + ModeTruncate RoundMode = 10 | |
63 | + // Ceiling is not supported now. | |
64 | + modeCeiling RoundMode = 0 | |
65 | + | |
66 | + MaxDecimalScale = 30 | |
67 | +) | |
68 | + | |
69 | +var ( | |
70 | + wordBufLen = 9 | |
71 | + mod9 = [128]int8{ | |
72 | + 0, 1, 2, 3, 4, 5, 6, 7, 8, | |
73 | + 0, 1, 2, 3, 4, 5, 6, 7, 8, | |
74 | + 0, 1, 2, 3, 4, 5, 6, 7, 8, | |
75 | + 0, 1, 2, 3, 4, 5, 6, 7, 8, | |
76 | + 0, 1, 2, 3, 4, 5, 6, 7, 8, | |
77 | + 0, 1, 2, 3, 4, 5, 6, 7, 8, | |
78 | + 0, 1, 2, 3, 4, 5, 6, 7, 8, | |
79 | + 0, 1, 2, 3, 4, 5, 6, 7, 8, | |
80 | + 0, 1, 2, 3, 4, 5, 6, 7, 8, | |
81 | + 0, 1, 2, 3, 4, 5, 6, 7, 8, | |
82 | + 0, 1, 2, 3, 4, 5, 6, 7, 8, | |
83 | + 0, 1, 2, 3, 4, 5, 6, 7, 8, | |
84 | + 0, 1, 2, 3, 4, 5, 6, 7, 8, | |
85 | + 0, 1, 2, 3, 4, 5, 6, 7, 8, | |
86 | + 0, 1, | |
87 | + } | |
88 | + div9 = [128]int{ | |
89 | + 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
90 | + 1, 1, 1, 1, 1, 1, 1, 1, 1, | |
91 | + 2, 2, 2, 2, 2, 2, 2, 2, 2, | |
92 | + 3, 3, 3, 3, 3, 3, 3, 3, 3, | |
93 | + 4, 4, 4, 4, 4, 4, 4, 4, 4, | |
94 | + 5, 5, 5, 5, 5, 5, 5, 5, 5, | |
95 | + 6, 6, 6, 6, 6, 6, 6, 6, 6, | |
96 | + 7, 7, 7, 7, 7, 7, 7, 7, 7, | |
97 | + 8, 8, 8, 8, 8, 8, 8, 8, 8, | |
98 | + 9, 9, 9, 9, 9, 9, 9, 9, 9, | |
99 | + 10, 10, 10, 10, 10, 10, 10, 10, 10, | |
100 | + 11, 11, 11, 11, 11, 11, 11, 11, 11, | |
101 | + 12, 12, 12, 12, 12, 12, 12, 12, 12, | |
102 | + 13, 13, 13, 13, 13, 13, 13, 13, 13, | |
103 | + 14, 14, | |
104 | + } | |
105 | + powers10 = [10]int32{ten0, ten1, ten2, ten3, ten4, ten5, ten6, ten7, ten8, ten9} | |
106 | + dig2bytes = [10]int{0, 1, 1, 2, 2, 3, 3, 4, 4, 4} | |
107 | + fracMax = [8]int32{ | |
108 | + 900000000, | |
109 | + 990000000, | |
110 | + 999000000, | |
111 | + 999900000, | |
112 | + 999990000, | |
113 | + 999999000, | |
114 | + 999999900, | |
115 | + 999999990, | |
116 | + } | |
117 | + zeroMyDecimal = MyDecimal{} | |
118 | +) | |
119 | + | |
120 | +// get the zero of MyDecimal with the specified result fraction digits | |
121 | +func zeroMyDecimalWithFrac(frac int8) MyDecimal { | |
122 | + zero := MyDecimal{} | |
123 | + zero.digitsFrac = frac | |
124 | + zero.resultFrac = frac | |
125 | + return zero | |
126 | +} | |
127 | + | |
128 | +// add adds a and b and carry, returns the sum and new carry. | |
129 | +func add(a, b, carry int32) (int32, int32) { | |
130 | + sum := a + b + carry | |
131 | + if sum >= wordBase { | |
132 | + carry = 1 | |
133 | + sum -= wordBase | |
134 | + } else { | |
135 | + carry = 0 | |
136 | + } | |
137 | + return sum, carry | |
138 | +} | |
139 | + | |
140 | +// add2 adds a and b and carry, returns the sum and new carry. | |
141 | +// It is only used in DecimalMul. | |
142 | +func add2(a, b, carry int32) (int32, int32) { | |
143 | + sum := int64(a) + int64(b) + int64(carry) | |
144 | + if sum >= wordBase { | |
145 | + carry = 1 | |
146 | + sum -= wordBase | |
147 | + } else { | |
148 | + carry = 0 | |
149 | + } | |
150 | + | |
151 | + if sum >= wordBase { | |
152 | + sum -= wordBase | |
153 | + carry++ | |
154 | + } | |
155 | + return int32(sum), carry | |
156 | +} | |
157 | + | |
158 | +// sub subtracts b and carry from a, returns the diff and new carry. | |
159 | +func sub(a, b, carry int32) (int32, int32) { | |
160 | + diff := a - b - carry | |
161 | + if diff < 0 { | |
162 | + carry = 1 | |
163 | + diff += wordBase | |
164 | + } else { | |
165 | + carry = 0 | |
166 | + } | |
167 | + return diff, carry | |
168 | +} | |
169 | + | |
170 | +// sub2 subtracts b and carry from a, returns the diff and new carry. | |
171 | +// the new carry may be 2. | |
172 | +func sub2(a, b, carry int32) (int32, int32) { | |
173 | + diff := a - b - carry | |
174 | + if diff < 0 { | |
175 | + carry = 1 | |
176 | + diff += wordBase | |
177 | + } else { | |
178 | + carry = 0 | |
179 | + } | |
180 | + if diff < 0 { | |
181 | + diff += wordBase | |
182 | + carry++ | |
183 | + } | |
184 | + return diff, carry | |
185 | +} | |
186 | + | |
187 | +// fixWordCntError limits word count in wordBufLen, and returns overflow or truncate error. | |
188 | +func fixWordCntError(wordsInt, wordsFrac int) (newWordsInt int, newWordsFrac int, err error) { | |
189 | + if wordsInt+wordsFrac > wordBufLen { | |
190 | + if wordsInt > wordBufLen { | |
191 | + return wordBufLen, 0, ErrOverflow | |
192 | + } | |
193 | + return wordsInt, wordBufLen - wordsInt, ErrTruncated | |
194 | + } | |
195 | + return wordsInt, wordsFrac, nil | |
196 | +} | |
197 | + | |
198 | +/* | |
199 | + countLeadingZeroes returns the number of leading zeroes that can be removed from fraction. | |
200 | + | |
201 | + @param i start index | |
202 | + @param word value to compare against list of powers of 10 | |
203 | +*/ | |
204 | +func countLeadingZeroes(i int, word int32) int { | |
205 | + leading := 0 | |
206 | + for word < powers10[i] { | |
207 | + i-- | |
208 | + leading++ | |
209 | + } | |
210 | + return leading | |
211 | +} | |
212 | + | |
213 | +/* | |
214 | + countTrailingZeros returns the number of trailing zeroes that can be removed from fraction. | |
215 | + | |
216 | + @param i start index | |
217 | + @param word value to compare against list of powers of 10 | |
218 | +*/ | |
219 | +func countTrailingZeroes(i int, word int32) int { | |
220 | + trailing := 0 | |
221 | + for word%powers10[i] == 0 { | |
222 | + i++ | |
223 | + trailing++ | |
224 | + } | |
225 | + return trailing | |
226 | +} | |
227 | + | |
228 | +func digitsToWords(digits int) int { | |
229 | + if digits+digitsPerWord-1 >= 0 && digits+digitsPerWord-1 < 128 { | |
230 | + return div9[digits+digitsPerWord-1] | |
231 | + } | |
232 | + return (digits + digitsPerWord - 1) / digitsPerWord | |
233 | +} | |
234 | + | |
235 | +// MyDecimalStructSize is the struct size of MyDecimal. | |
236 | +const MyDecimalStructSize = 40 | |
237 | + | |
238 | +// MyDecimal represents a decimal value. | |
239 | +type MyDecimal struct { | |
240 | + digitsInt int8 // the number of *decimal* digits before the point. | |
241 | + | |
242 | + digitsFrac int8 // the number of decimal digits after the point. | |
243 | + | |
244 | + resultFrac int8 // result fraction digits. | |
245 | + | |
246 | + negative bool | |
247 | + | |
248 | + // wordBuf is an array of int32 words. | |
249 | + // A word is an int32 value can hold 9 digits.(0 <= word < wordBase) | |
250 | + wordBuf [maxWordBufLen]int32 | |
251 | +} | |
252 | + | |
253 | +// IsNegative returns whether a decimal is negative. | |
254 | +func (d *MyDecimal) IsNegative() bool { | |
255 | + return d.negative | |
256 | +} | |
257 | + | |
258 | +// GetDigitsFrac returns the digitsFrac. | |
259 | +func (d *MyDecimal) GetDigitsFrac() int8 { | |
260 | + return d.digitsFrac | |
261 | +} | |
262 | + | |
263 | +// GetDigitsInt returns the digitsInt. | |
264 | +func (d *MyDecimal) GetDigitsInt() int8 { | |
265 | + return d.digitsInt | |
266 | +} | |
267 | + | |
268 | +// String returns the decimal string representation rounded to resultFrac. | |
269 | +func (d *MyDecimal) String() string { | |
270 | + tmp := *d | |
271 | + err := tmp.Round(&tmp, int(tmp.resultFrac), ModeHalfEven) | |
272 | + Log(err) | |
273 | + return string(tmp.ToString()) | |
274 | +} | |
275 | + | |
276 | +func (d *MyDecimal) stringSize() int { | |
277 | + // sign, zero integer and dot. | |
278 | + return int(d.digitsInt + d.digitsFrac + 3) | |
279 | +} | |
280 | + | |
281 | +func (d *MyDecimal) removeLeadingZeros() (wordIdx int, digitsInt int) { | |
282 | + digitsInt = int(d.digitsInt) | |
283 | + i := ((digitsInt - 1) % digitsPerWord) + 1 | |
284 | + for digitsInt > 0 && d.wordBuf[wordIdx] == 0 { | |
285 | + digitsInt -= i | |
286 | + i = digitsPerWord | |
287 | + wordIdx++ | |
288 | + } | |
289 | + if digitsInt > 0 { | |
290 | + digitsInt -= countLeadingZeroes((digitsInt-1)%digitsPerWord, d.wordBuf[wordIdx]) | |
291 | + } else { | |
292 | + digitsInt = 0 | |
293 | + } | |
294 | + return | |
295 | +} | |
296 | + | |
297 | +func (d *MyDecimal) removeTrailingZeros() (lastWordIdx int, digitsFrac int) { | |
298 | + digitsFrac = int(d.digitsFrac) | |
299 | + i := ((digitsFrac - 1) % digitsPerWord) + 1 | |
300 | + lastWordIdx = digitsToWords(int(d.digitsInt)) + digitsToWords(int(d.digitsFrac)) | |
301 | + for digitsFrac > 0 && d.wordBuf[lastWordIdx-1] == 0 { | |
302 | + digitsFrac -= i | |
303 | + i = digitsPerWord | |
304 | + lastWordIdx-- | |
305 | + } | |
306 | + if digitsFrac > 0 { | |
307 | + digitsFrac -= countTrailingZeroes(9-((digitsFrac-1)%digitsPerWord), d.wordBuf[lastWordIdx-1]) | |
308 | + } else { | |
309 | + digitsFrac = 0 | |
310 | + } | |
311 | + return | |
312 | +} | |
313 | + | |
314 | +// ToString converts decimal to its printable string representation without rounding. | |
315 | +// | |
316 | +// RETURN VALUE | |
317 | +// | |
318 | +// str - result string | |
319 | +// errCode - eDecOK/eDecTruncate/eDecOverflow | |
320 | +// | |
321 | +func (d *MyDecimal) ToString() (str []byte) { | |
322 | + str = make([]byte, d.stringSize()) | |
323 | + digitsFrac := int(d.digitsFrac) | |
324 | + wordStartIdx, digitsInt := d.removeLeadingZeros() | |
325 | + if digitsInt+digitsFrac == 0 { | |
326 | + digitsInt = 1 | |
327 | + wordStartIdx = 0 | |
328 | + } | |
329 | + | |
330 | + digitsIntLen := digitsInt | |
331 | + if digitsIntLen == 0 { | |
332 | + digitsIntLen = 1 | |
333 | + } | |
334 | + digitsFracLen := digitsFrac | |
335 | + length := digitsIntLen + digitsFracLen | |
336 | + if d.negative { | |
337 | + length++ | |
338 | + } | |
339 | + if digitsFrac > 0 { | |
340 | + length++ | |
341 | + } | |
342 | + str = str[:length] | |
343 | + strIdx := 0 | |
344 | + if d.negative { | |
345 | + str[strIdx] = '-' | |
346 | + strIdx++ | |
347 | + } | |
348 | + var fill int | |
349 | + if digitsFrac > 0 { | |
350 | + fracIdx := strIdx + digitsIntLen | |
351 | + fill = digitsFracLen - digitsFrac | |
352 | + wordIdx := wordStartIdx + digitsToWords(digitsInt) | |
353 | + str[fracIdx] = '.' | |
354 | + fracIdx++ | |
355 | + for ; digitsFrac > 0; digitsFrac -= digitsPerWord { | |
356 | + x := d.wordBuf[wordIdx] | |
357 | + wordIdx++ | |
358 | + for i := myMin(digitsFrac, digitsPerWord); i > 0; i-- { | |
359 | + y := x / digMask | |
360 | + str[fracIdx] = byte(y) + '0' | |
361 | + fracIdx++ | |
362 | + x -= y * digMask | |
363 | + x *= 10 | |
364 | + } | |
365 | + } | |
366 | + for ; fill > 0; fill-- { | |
367 | + str[fracIdx] = '0' | |
368 | + fracIdx++ | |
369 | + } | |
370 | + } | |
371 | + fill = digitsIntLen - digitsInt | |
372 | + if digitsInt == 0 { | |
373 | + fill-- /* symbol 0 before digital point */ | |
374 | + } | |
375 | + for ; fill > 0; fill-- { | |
376 | + str[strIdx] = '0' | |
377 | + strIdx++ | |
378 | + } | |
379 | + if digitsInt > 0 { | |
380 | + strIdx += digitsInt | |
381 | + wordIdx := wordStartIdx + digitsToWords(digitsInt) | |
382 | + for ; digitsInt > 0; digitsInt -= digitsPerWord { | |
383 | + wordIdx-- | |
384 | + x := d.wordBuf[wordIdx] | |
385 | + for i := myMin(digitsInt, digitsPerWord); i > 0; i-- { | |
386 | + y := x / 10 | |
387 | + strIdx-- | |
388 | + str[strIdx] = '0' + byte(x-y*10) | |
389 | + x = y | |
390 | + } | |
391 | + } | |
392 | + } else { | |
393 | + str[strIdx] = '0' | |
394 | + } | |
395 | + return | |
396 | +} | |
397 | + | |
398 | +// FromString parses decimal from string. | |
399 | +func (d *MyDecimal) FromString(str []byte) error { | |
400 | + for i := 0; i < len(str); i++ { | |
401 | + if !isSpace(str[i]) { | |
402 | + str = str[i:] | |
403 | + break | |
404 | + } | |
405 | + } | |
406 | + if len(str) == 0 { | |
407 | + *d = zeroMyDecimal | |
408 | + return ErrBadNumber | |
409 | + } | |
410 | + switch str[0] { | |
411 | + case '-': | |
412 | + d.negative = true | |
413 | + fallthrough | |
414 | + case '+': | |
415 | + str = str[1:] | |
416 | + } | |
417 | + var strIdx int | |
418 | + for strIdx < len(str) && isDigit(str[strIdx]) { | |
419 | + strIdx++ | |
420 | + } | |
421 | + digitsInt := strIdx | |
422 | + var digitsFrac int | |
423 | + var endIdx int | |
424 | + if strIdx < len(str) && str[strIdx] == '.' { | |
425 | + endIdx = strIdx + 1 | |
426 | + for endIdx < len(str) && isDigit(str[endIdx]) { | |
427 | + endIdx++ | |
428 | + } | |
429 | + digitsFrac = endIdx - strIdx - 1 | |
430 | + } else { | |
431 | + digitsFrac = 0 | |
432 | + endIdx = strIdx | |
433 | + } | |
434 | + if digitsInt+digitsFrac == 0 { | |
435 | + *d = zeroMyDecimal | |
436 | + return ErrBadNumber | |
437 | + } | |
438 | + wordsInt := digitsToWords(digitsInt) | |
439 | + wordsFrac := digitsToWords(digitsFrac) | |
440 | + wordsInt, wordsFrac, err := fixWordCntError(wordsInt, wordsFrac) | |
441 | + if err != nil { | |
442 | + digitsFrac = wordsFrac * digitsPerWord | |
443 | + if err == ErrOverflow { | |
444 | + digitsInt = wordsInt * digitsPerWord | |
445 | + } | |
446 | + } | |
447 | + d.digitsInt = int8(digitsInt) | |
448 | + d.digitsFrac = int8(digitsFrac) | |
449 | + wordIdx := wordsInt | |
450 | + strIdxTmp := strIdx | |
451 | + var word int32 | |
452 | + var innerIdx int | |
453 | + for digitsInt > 0 { | |
454 | + digitsInt-- | |
455 | + strIdx-- | |
456 | + word += int32(str[strIdx]-'0') * powers10[innerIdx] | |
457 | + innerIdx++ | |
458 | + if innerIdx == digitsPerWord { | |
459 | + wordIdx-- | |
460 | + d.wordBuf[wordIdx] = word | |
461 | + word = 0 | |
462 | + innerIdx = 0 | |
463 | + } | |
464 | + } | |
465 | + if innerIdx != 0 { | |
466 | + wordIdx-- | |
467 | + d.wordBuf[wordIdx] = word | |
468 | + } | |
469 | + | |
470 | + wordIdx = wordsInt | |
471 | + strIdx = strIdxTmp | |
472 | + word = 0 | |
473 | + innerIdx = 0 | |
474 | + for digitsFrac > 0 { | |
475 | + digitsFrac-- | |
476 | + strIdx++ | |
477 | + word = int32(str[strIdx]-'0') + word*10 | |
478 | + innerIdx++ | |
479 | + if innerIdx == digitsPerWord { | |
480 | + d.wordBuf[wordIdx] = word | |
481 | + wordIdx++ | |
482 | + word = 0 | |
483 | + innerIdx = 0 | |
484 | + } | |
485 | + } | |
486 | + if innerIdx != 0 { | |
487 | + d.wordBuf[wordIdx] = word * powers10[digitsPerWord-innerIdx] | |
488 | + } | |
489 | + if endIdx+1 <= len(str) && (str[endIdx] == 'e' || str[endIdx] == 'E') { | |
490 | + exponent, err1 := strToInt(string(str[endIdx+1:])) | |
491 | + if err1 != nil { | |
492 | + err = err1 | |
493 | + if err != ErrTruncated { | |
494 | + *d = zeroMyDecimal | |
495 | + } | |
496 | + } | |
497 | + if exponent > math.MaxInt32/2 { | |
498 | + negative := d.negative | |
499 | + maxDecimal(wordBufLen*digitsPerWord, 0, d) | |
500 | + d.negative = negative | |
501 | + err = ErrOverflow | |
502 | + } | |
503 | + if exponent < math.MinInt32/2 && err != ErrOverflow { | |
504 | + *d = zeroMyDecimal | |
505 | + err = ErrTruncated | |
506 | + } | |
507 | + if err != ErrOverflow { | |
508 | + shiftErr := d.Shift(int(exponent)) | |
509 | + if shiftErr != nil { | |
510 | + if shiftErr == ErrOverflow { | |
511 | + negative := d.negative | |
512 | + maxDecimal(wordBufLen*digitsPerWord, 0, d) | |
513 | + d.negative = negative | |
514 | + } | |
515 | + err = shiftErr | |
516 | + } | |
517 | + } | |
518 | + } | |
519 | + allZero := true | |
520 | + for i := 0; i < wordBufLen; i++ { | |
521 | + if d.wordBuf[i] != 0 { | |
522 | + allZero = false | |
523 | + break | |
524 | + } | |
525 | + } | |
526 | + if allZero { | |
527 | + d.negative = false | |
528 | + } | |
529 | + d.resultFrac = d.digitsFrac | |
530 | + return err | |
531 | +} | |
532 | + | |
533 | +// Shift shifts decimal digits in given number (with rounding if it need), shift > 0 means shift to left shift, | |
534 | +// shift < 0 means right shift. In fact it is multiplying on 10^shift. | |
535 | +// | |
536 | +// RETURN | |
537 | +// eDecOK OK | |
538 | +// eDecOverflow operation lead to overflow, number is untoched | |
539 | +// eDecTruncated number was rounded to fit into buffer | |
540 | +// | |
541 | +func (d *MyDecimal) Shift(shift int) error { | |
542 | + var err error | |
543 | + if shift == 0 { | |
544 | + return nil | |
545 | + } | |
546 | + var ( | |
547 | + // digitBegin is index of first non zero digit (all indexes from 0). | |
548 | + digitBegin int | |
549 | + // digitEnd is index of position after last decimal digit. | |
550 | + digitEnd int | |
551 | + // point is index of digit position just after point. | |
552 | + point = digitsToWords(int(d.digitsInt)) * digitsPerWord | |
553 | + // new point position. | |
554 | + newPoint = point + shift | |
555 | + // number of digits in result. | |
556 | + digitsInt, digitsFrac int | |
557 | + newFront int | |
558 | + ) | |
559 | + digitBegin, digitEnd = d.digitBounds() | |
560 | + if digitBegin == digitEnd { | |
561 | + *d = zeroMyDecimal | |
562 | + return nil | |
563 | + } | |
564 | + | |
565 | + digitsInt = newPoint - digitBegin | |
566 | + if digitsInt < 0 { | |
567 | + digitsInt = 0 | |
568 | + } | |
569 | + digitsFrac = digitEnd - newPoint | |
570 | + if digitsFrac < 0 { | |
571 | + digitsFrac = 0 | |
572 | + } | |
573 | + wordsInt := digitsToWords(digitsInt) | |
574 | + wordsFrac := digitsToWords(digitsFrac) | |
575 | + newLen := wordsInt + wordsFrac | |
576 | + if newLen > wordBufLen { | |
577 | + lack := newLen - wordBufLen | |
578 | + if wordsFrac < lack { | |
579 | + return ErrOverflow | |
580 | + } | |
581 | + /* cut off fraction part to allow new number to fit in our buffer */ | |
582 | + err = ErrTruncated | |
583 | + wordsFrac -= lack | |
584 | + diff := digitsFrac - wordsFrac*digitsPerWord | |
585 | + err1 := d.Round(d, digitEnd-point-diff, ModeHalfEven) | |
586 | + if err1 != nil { | |
587 | + return err1 | |
588 | + } | |
589 | + digitEnd -= diff | |
590 | + digitsFrac = wordsFrac * digitsPerWord | |
591 | + if digitEnd <= digitBegin { | |
592 | + /* | |
593 | + We lost all digits (they will be shifted out of buffer), so we can | |
594 | + just return 0. | |
595 | + */ | |
596 | + *d = zeroMyDecimal | |
597 | + return ErrTruncated | |
598 | + } | |
599 | + } | |
600 | + | |
601 | + if shift%digitsPerWord != 0 { | |
602 | + var lMiniShift, rMiniShift, miniShift int | |
603 | + var doLeft bool | |
604 | + /* | |
605 | + Calculate left/right shift to align decimal digits inside our bug | |
606 | + digits correctly. | |
607 | + */ | |
608 | + if shift > 0 { | |
609 | + lMiniShift = shift % digitsPerWord | |
610 | + rMiniShift = digitsPerWord - lMiniShift | |
611 | + doLeft = lMiniShift <= digitBegin | |
612 | + } else { | |
613 | + rMiniShift = (-shift) % digitsPerWord | |
614 | + lMiniShift = digitsPerWord - rMiniShift | |
615 | + doLeft = (digitsPerWord*wordBufLen - digitEnd) < rMiniShift | |
616 | + } | |
617 | + if doLeft { | |
618 | + d.doMiniLeftShift(lMiniShift, digitBegin, digitEnd) | |
619 | + miniShift = -lMiniShift | |
620 | + } else { | |
621 | + d.doMiniRightShift(rMiniShift, digitBegin, digitEnd) | |
622 | + miniShift = rMiniShift | |
623 | + } | |
624 | + newPoint += miniShift | |
625 | + /* | |
626 | + If number is shifted and correctly aligned in buffer we can finish. | |
627 | + */ | |
628 | + if shift+miniShift == 0 && (newPoint-digitsInt) < digitsPerWord { | |
629 | + d.digitsInt = int8(digitsInt) | |
630 | + d.digitsFrac = int8(digitsFrac) | |
631 | + return err /* already shifted as it should be */ | |
632 | + } | |
633 | + digitBegin += miniShift | |
634 | + digitEnd += miniShift | |
635 | + } | |
636 | + | |
637 | + /* if new 'decimal front' is in first digit, we do not need move digits */ | |
638 | + newFront = newPoint - digitsInt | |
639 | + if newFront >= digitsPerWord || newFront < 0 { | |
640 | + /* need to move digits */ | |
641 | + var wordShift int | |
642 | + if newFront > 0 { | |
643 | + /* move left */ | |
644 | + wordShift = newFront / digitsPerWord | |
645 | + to := digitBegin/digitsPerWord - wordShift | |
646 | + barier := (digitEnd-1)/digitsPerWord - wordShift | |
647 | + for ; to <= barier; to++ { | |
648 | + d.wordBuf[to] = d.wordBuf[to+wordShift] | |
649 | + } | |
650 | + for barier += wordShift; to <= barier; to++ { | |
651 | + d.wordBuf[to] = 0 | |
652 | + } | |
653 | + wordShift = -wordShift | |
654 | + } else { | |
655 | + /* move right */ | |
656 | + wordShift = (1 - newFront) / digitsPerWord | |
657 | + to := (digitEnd-1)/digitsPerWord + wordShift | |
658 | + barier := digitBegin/digitsPerWord + wordShift | |
659 | + for ; to >= barier; to-- { | |
660 | + d.wordBuf[to] = d.wordBuf[to-wordShift] | |
661 | + } | |
662 | + for barier -= wordShift; to >= barier; to-- { | |
663 | + d.wordBuf[to] = 0 | |
664 | + } | |
665 | + } | |
666 | + digitShift := wordShift * digitsPerWord | |
667 | + digitBegin += digitShift | |
668 | + digitEnd += digitShift | |
669 | + newPoint += digitShift | |
670 | + } | |
671 | + /* | |
672 | + If there are gaps then fill them with 0. | |
673 | + | |
674 | + Only one of following 'for' loops will work because wordIdxBegin <= wordIdxEnd. | |
675 | + */ | |
676 | + wordIdxBegin := digitBegin / digitsPerWord | |
677 | + wordIdxEnd := (digitEnd - 1) / digitsPerWord | |
678 | + wordIdxNewPoint := 0 | |
679 | + | |
680 | + /* We don't want negative new_point below */ | |
681 | + if newPoint != 0 { | |
682 | + wordIdxNewPoint = (newPoint - 1) / digitsPerWord | |
683 | + } | |
684 | + if wordIdxNewPoint > wordIdxEnd { | |
685 | + for wordIdxNewPoint > wordIdxEnd { | |
686 | + d.wordBuf[wordIdxNewPoint] = 0 | |
687 | + wordIdxNewPoint-- | |
688 | + } | |
689 | + } else { | |
690 | + for ; wordIdxNewPoint < wordIdxBegin; wordIdxNewPoint++ { | |
691 | + d.wordBuf[wordIdxNewPoint] = 0 | |
692 | + } | |
693 | + } | |
694 | + d.digitsInt = int8(digitsInt) | |
695 | + d.digitsFrac = int8(digitsFrac) | |
696 | + return err | |
697 | +} | |
698 | + | |
699 | +/* | |
700 | + digitBounds returns bounds of decimal digits in the number. | |
701 | + | |
702 | + start - index (from 0 ) of first decimal digits. | |
703 | + end - index of position just after last decimal digit. | |
704 | +*/ | |
705 | +func (d *MyDecimal) digitBounds() (start, end int) { | |
706 | + var i int | |
707 | + bufBeg := 0 | |
708 | + bufLen := digitsToWords(int(d.digitsInt)) + digitsToWords(int(d.digitsFrac)) | |
709 | + bufEnd := bufLen - 1 | |
710 | + | |
711 | + /* find non-zero digit from number beginning */ | |
712 | + for bufBeg < bufLen && d.wordBuf[bufBeg] == 0 { | |
713 | + bufBeg++ | |
714 | + } | |
715 | + if bufBeg >= bufLen { | |
716 | + return 0, 0 | |
717 | + } | |
718 | + | |
719 | + /* find non-zero decimal digit from number beginning */ | |
720 | + if bufBeg == 0 && d.digitsInt > 0 { | |
721 | + i = (int(d.digitsInt) - 1) % digitsPerWord | |
722 | + start = digitsPerWord - i - 1 | |
723 | + } else { | |
724 | + i = digitsPerWord - 1 | |
725 | + start = bufBeg * digitsPerWord | |
726 | + } | |
727 | + if bufBeg < bufLen { | |
728 | + start += countLeadingZeroes(i, d.wordBuf[bufBeg]) | |
729 | + } | |
730 | + | |
731 | + /* find non-zero digit at the end */ | |
732 | + for bufEnd > bufBeg && d.wordBuf[bufEnd] == 0 { | |
733 | + bufEnd-- | |
734 | + } | |
735 | + /* find non-zero decimal digit from the end */ | |
736 | + if bufEnd == bufLen-1 && d.digitsFrac > 0 { | |
737 | + i = (int(d.digitsFrac)-1)%digitsPerWord + 1 | |
738 | + end = bufEnd*digitsPerWord + i | |
739 | + i = digitsPerWord - i + 1 | |
740 | + } else { | |
741 | + end = (bufEnd + 1) * digitsPerWord | |
742 | + i = 1 | |
743 | + } | |
744 | + end -= countTrailingZeroes(i, d.wordBuf[bufEnd]) | |
745 | + return start, end | |
746 | +} | |
747 | + | |
748 | +/* | |
749 | + doMiniLeftShift does left shift for alignment of data in buffer. | |
750 | + | |
751 | + shift number of decimal digits on which it should be shifted | |
752 | + beg/end bounds of decimal digits (see digitsBounds()) | |
753 | + | |
754 | + NOTE | |
755 | + Result fitting in the buffer should be garanted. | |
756 | + 'shift' have to be from 1 to digitsPerWord-1 (inclusive) | |
757 | +*/ | |
758 | +func (d *MyDecimal) doMiniLeftShift(shift, beg, end int) { | |
759 | + bufFrom := beg / digitsPerWord | |
760 | + bufEnd := (end - 1) / digitsPerWord | |
761 | + cShift := digitsPerWord - shift | |
762 | + if beg%digitsPerWord < shift { | |
763 | + d.wordBuf[bufFrom-1] = d.wordBuf[bufFrom] / powers10[cShift] | |
764 | + } | |
765 | + for bufFrom < bufEnd { | |
766 | + d.wordBuf[bufFrom] = (d.wordBuf[bufFrom]%powers10[cShift])*powers10[shift] + d.wordBuf[bufFrom+1]/powers10[cShift] | |
767 | + bufFrom++ | |
768 | + } | |
769 | + d.wordBuf[bufFrom] = (d.wordBuf[bufFrom] % powers10[cShift]) * powers10[shift] | |
770 | +} | |
771 | + | |
772 | +/* | |
773 | + doMiniRightShift does right shift for alignment of data in buffer. | |
774 | + | |
775 | + shift number of decimal digits on which it should be shifted | |
776 | + beg/end bounds of decimal digits (see digitsBounds()) | |
777 | + | |
778 | + NOTE | |
779 | + Result fitting in the buffer should be garanted. | |
780 | + 'shift' have to be from 1 to digitsPerWord-1 (inclusive) | |
781 | +*/ | |
782 | +func (d *MyDecimal) doMiniRightShift(shift, beg, end int) { | |
783 | + bufFrom := (end - 1) / digitsPerWord | |
784 | + bufEnd := beg / digitsPerWord | |
785 | + cShift := digitsPerWord - shift | |
786 | + if digitsPerWord-((end-1)%digitsPerWord+1) < shift { | |
787 | + d.wordBuf[bufFrom+1] = (d.wordBuf[bufFrom] % powers10[shift]) * powers10[cShift] | |
788 | + } | |
789 | + for bufFrom > bufEnd { | |
790 | + d.wordBuf[bufFrom] = d.wordBuf[bufFrom]/powers10[shift] + (d.wordBuf[bufFrom-1]%powers10[shift])*powers10[cShift] | |
791 | + bufFrom-- | |
792 | + } | |
793 | + d.wordBuf[bufFrom] = d.wordBuf[bufFrom] / powers10[shift] | |
794 | +} | |
795 | + | |
796 | +// Round rounds the decimal to "frac" digits. | |
797 | +// | |
798 | +// to - result buffer. d == to is allowed | |
799 | +// frac - to what position after fraction point to round. can be negative! | |
800 | +// roundMode - round to nearest even or truncate | |
801 | +// ModeHalfEven rounds normally. | |
802 | +// Truncate just truncates the decimal. | |
803 | +// | |
804 | +// NOTES | |
805 | +// scale can be negative ! | |
806 | +// one TRUNCATED error (line XXX below) isn't treated very logical :( | |
807 | +// | |
808 | +// RETURN VALUE | |
809 | +// eDecOK/eDecTruncated | |
810 | +func (d *MyDecimal) Round(to *MyDecimal, frac int, roundMode RoundMode) (err error) { | |
811 | + // wordsFracTo is the number of fraction words in buffer. | |
812 | + wordsFracTo := (frac + 1) / digitsPerWord | |
813 | + if frac > 0 { | |
814 | + wordsFracTo = digitsToWords(frac) | |
815 | + } | |
816 | + wordsFrac := digitsToWords(int(d.digitsFrac)) | |
817 | + wordsInt := digitsToWords(int(d.digitsInt)) | |
818 | + | |
819 | + roundDigit := int32(roundMode) | |
820 | + /* TODO - fix this code as it won't work for CEILING mode */ | |
821 | + | |
822 | + if wordsInt+wordsFracTo > wordBufLen { | |
823 | + wordsFracTo = wordBufLen - wordsInt | |
824 | + frac = wordsFracTo * digitsPerWord | |
825 | + err = ErrTruncated | |
826 | + } | |
827 | + if int(d.digitsInt)+frac < 0 { | |
828 | + *to = zeroMyDecimal | |
829 | + return nil | |
830 | + } | |
831 | + if to != d { | |
832 | + copy(to.wordBuf[:], d.wordBuf[:]) | |
833 | + to.negative = d.negative | |
834 | + to.digitsInt = int8(myMin(wordsInt, wordBufLen) * digitsPerWord) | |
835 | + } | |
836 | + if wordsFracTo > wordsFrac { | |
837 | + idx := wordsInt + wordsFrac | |
838 | + for wordsFracTo > wordsFrac { | |
839 | + wordsFracTo-- | |
840 | + to.wordBuf[idx] = 0 | |
841 | + idx++ | |
842 | + } | |
843 | + to.digitsFrac = int8(frac) | |
844 | + to.resultFrac = to.digitsFrac | |
845 | + return | |
846 | + } | |
847 | + if frac >= int(d.digitsFrac) { | |
848 | + to.digitsFrac = int8(frac) | |
849 | + to.resultFrac = to.digitsFrac | |
850 | + return | |
851 | + } | |
852 | + | |
853 | + // Do increment. | |
854 | + toIdx := wordsInt + wordsFracTo - 1 | |
855 | + if frac == wordsFracTo*digitsPerWord { | |
856 | + doInc := false | |
857 | + switch roundMode { | |
858 | + // Notice: No support for ceiling mode now. | |
859 | + case modeCeiling: | |
860 | + // If any word after scale is not zero, do increment. | |
861 | + // e.g ceiling 3.0001 to scale 1, gets 3.1 | |
862 | + idx := toIdx + (wordsFrac - wordsFracTo) | |
863 | + for idx > toIdx { | |
864 | + if d.wordBuf[idx] != 0 { | |
865 | + doInc = true | |
866 | + break | |
867 | + } | |
868 | + idx-- | |
869 | + } | |
870 | + case ModeHalfEven: | |
871 | + digAfterScale := d.wordBuf[toIdx+1] / digMask // the first digit after scale. | |
872 | + // If first digit after scale is 5 and round even, do increment if digit at scale is odd. | |
873 | + doInc = (digAfterScale > 5) || (digAfterScale == 5) | |
874 | + case ModeTruncate: | |
875 | + // Never round, just truncate. | |
876 | + doInc = false | |
877 | + } | |
878 | + if doInc { | |
879 | + if toIdx >= 0 { | |
880 | + to.wordBuf[toIdx]++ | |
881 | + } else { | |
882 | + toIdx++ | |
883 | + to.wordBuf[toIdx] = wordBase | |
884 | + } | |
885 | + } else if wordsInt+wordsFracTo == 0 { | |
886 | + *to = zeroMyDecimal | |
887 | + return nil | |
888 | + } | |
889 | + } else { | |
890 | + /* TODO - fix this code as it won't work for CEILING mode */ | |
891 | + pos := wordsFracTo*digitsPerWord - frac - 1 | |
892 | + shiftedNumber := to.wordBuf[toIdx] / powers10[pos] | |
893 | + digAfterScale := shiftedNumber % 10 | |
894 | + if digAfterScale > roundDigit || (roundDigit == 5 && digAfterScale == 5) { | |
895 | + shiftedNumber += 10 | |
896 | + } | |
897 | + to.wordBuf[toIdx] = powers10[pos] * (shiftedNumber - digAfterScale) | |
898 | + } | |
899 | + /* | |
900 | + In case we're rounding e.g. 1.5e9 to 2.0e9, the decimal words inside | |
901 | + the buffer are as follows. | |
902 | + | |
903 | + Before <1, 5e8> | |
904 | + After <2, 5e8> | |
905 | + | |
906 | + Hence we need to set the 2nd field to 0. | |
907 | + The same holds if we round 1.5e-9 to 2e-9. | |
908 | + */ | |
909 | + if wordsFracTo < wordsFrac { | |
910 | + idx := wordsInt + wordsFracTo | |
911 | + if frac == 0 && wordsInt == 0 { | |
912 | + idx = 1 | |
913 | + } | |
914 | + for idx < wordBufLen { | |
915 | + to.wordBuf[idx] = 0 | |
916 | + idx++ | |
917 | + } | |
918 | + } | |
919 | + | |
920 | + // Handle carry. | |
921 | + var carry int32 | |
922 | + if to.wordBuf[toIdx] >= wordBase { | |
923 | + carry = 1 | |
924 | + to.wordBuf[toIdx] -= wordBase | |
925 | + for carry == 1 && toIdx > 0 { | |
926 | + toIdx-- | |
927 | + to.wordBuf[toIdx], carry = add(to.wordBuf[toIdx], 0, carry) | |
928 | + } | |
929 | + if carry > 0 { | |
930 | + if wordsInt+wordsFracTo >= wordBufLen { | |
931 | + wordsFracTo-- | |
932 | + frac = wordsFracTo * digitsPerWord | |
933 | + err = ErrTruncated | |
934 | + } | |
935 | + for toIdx = wordsInt + myMax(wordsFracTo, 0); toIdx > 0; toIdx-- { | |
936 | + if toIdx < wordBufLen { | |
937 | + to.wordBuf[toIdx] = to.wordBuf[toIdx-1] | |
938 | + } else { | |
939 | + err = ErrOverflow | |
940 | + } | |
941 | + } | |
942 | + to.wordBuf[toIdx] = 1 | |
943 | + /* We cannot have more than 9 * 9 = 81 digits. */ | |
944 | + if int(to.digitsInt) < digitsPerWord*wordBufLen { | |
945 | + to.digitsInt++ | |
946 | + } else { | |
947 | + err = ErrOverflow | |
948 | + } | |
949 | + } | |
950 | + } else { | |
951 | + for { | |
952 | + if to.wordBuf[toIdx] != 0 { | |
953 | + break | |
954 | + } | |
955 | + if toIdx == 0 { | |
956 | + /* making 'zero' with the proper scale */ | |
957 | + idx := wordsFracTo + 1 | |
958 | + to.digitsInt = 1 | |
959 | + to.digitsFrac = int8(myMax(frac, 0)) | |
960 | + to.negative = false | |
961 | + for toIdx < idx { | |
962 | + to.wordBuf[toIdx] = 0 | |
963 | + toIdx++ | |
964 | + } | |
965 | + to.resultFrac = to.digitsFrac | |
966 | + return nil | |
967 | + } | |
968 | + toIdx-- | |
969 | + } | |
970 | + } | |
971 | + /* Here we check 999.9 -> 1000 case when we need to increase intDigCnt */ | |
972 | + firstDig := mod9[to.digitsInt] | |
973 | + if firstDig > 0 && to.wordBuf[toIdx] >= powers10[firstDig] { | |
974 | + to.digitsInt++ | |
975 | + } | |
976 | + if frac < 0 { | |
977 | + frac = 0 | |
978 | + } | |
979 | + to.digitsFrac = int8(frac) | |
980 | + to.resultFrac = to.digitsFrac | |
981 | + return | |
982 | +} | |
983 | + | |
984 | +// FromInt sets the decimal value from int64. | |
985 | +func (d *MyDecimal) FromInt(val int64) *MyDecimal { | |
986 | + var uVal uint64 | |
987 | + if val < 0 { | |
988 | + d.negative = true | |
989 | + uVal = uint64(-val) | |
990 | + } else { | |
991 | + uVal = uint64(val) | |
992 | + } | |
993 | + return d.FromUint(uVal) | |
994 | +} | |
995 | + | |
996 | +// FromUint sets the decimal value from uint64. | |
997 | +func (d *MyDecimal) FromUint(val uint64) *MyDecimal { | |
998 | + x := val | |
999 | + wordIdx := 1 | |
1000 | + for x >= wordBase { | |
1001 | + wordIdx++ | |
1002 | + x /= wordBase | |
1003 | + } | |
1004 | + d.digitsFrac = 0 | |
1005 | + d.digitsInt = int8(wordIdx * digitsPerWord) | |
1006 | + x = val | |
1007 | + for wordIdx > 0 { | |
1008 | + wordIdx-- | |
1009 | + y := x / wordBase | |
1010 | + d.wordBuf[wordIdx] = int32(x - y*wordBase) | |
1011 | + x = y | |
1012 | + } | |
1013 | + return d | |
1014 | +} | |
1015 | + | |
1016 | +// ToInt returns int part of the decimal, returns the result and errcode. | |
1017 | +func (d *MyDecimal) ToInt() (int64, error) { | |
1018 | + var x int64 | |
1019 | + wordIdx := 0 | |
1020 | + for i := d.digitsInt; i > 0; i -= digitsPerWord { | |
1021 | + y := x | |
1022 | + /* | |
1023 | + Attention: trick! | |
1024 | + we're calculating -|from| instead of |from| here | |
1025 | + because |LONGLONG_MIN| > LONGLONG_MAX | |
1026 | + so we can convert -9223372036854775808 correctly | |
1027 | + */ | |
1028 | + x = x*wordBase - int64(d.wordBuf[wordIdx]) | |
1029 | + wordIdx++ | |
1030 | + if y < math.MinInt64/wordBase || x > y { | |
1031 | + /* | |
1032 | + the decimal is bigger than any possible integer | |
1033 | + return border integer depending on the sign | |
1034 | + */ | |
1035 | + if d.negative { | |
1036 | + return math.MinInt64, ErrOverflow | |
1037 | + } | |
1038 | + return math.MaxInt64, ErrOverflow | |
1039 | + } | |
1040 | + } | |
1041 | + /* boundary case: 9223372036854775808 */ | |
1042 | + if !d.negative && x == math.MinInt64 { | |
1043 | + return math.MaxInt64, ErrOverflow | |
1044 | + } | |
1045 | + if !d.negative { | |
1046 | + x = -x | |
1047 | + } | |
1048 | + for i := d.digitsFrac; i > 0; i -= digitsPerWord { | |
1049 | + if d.wordBuf[wordIdx] != 0 { | |
1050 | + return x, ErrTruncated | |
1051 | + } | |
1052 | + wordIdx++ | |
1053 | + } | |
1054 | + return x, nil | |
1055 | +} | |
1056 | + | |
1057 | +// ToUint returns int part of the decimal, returns the result and errcode. | |
1058 | +func (d *MyDecimal) ToUint() (uint64, error) { | |
1059 | + if d.negative { | |
1060 | + return 0, ErrOverflow | |
1061 | + } | |
1062 | + var x uint64 | |
1063 | + wordIdx := 0 | |
1064 | + for i := d.digitsInt; i > 0; i -= digitsPerWord { | |
1065 | + y := x | |
1066 | + x = x*wordBase + uint64(d.wordBuf[wordIdx]) | |
1067 | + wordIdx++ | |
1068 | + if y > math.MaxUint64/wordBase || x < y { | |
1069 | + return math.MaxUint64, ErrOverflow | |
1070 | + } | |
1071 | + } | |
1072 | + for i := d.digitsFrac; i > 0; i -= digitsPerWord { | |
1073 | + if d.wordBuf[wordIdx] != 0 { | |
1074 | + return x, ErrTruncated | |
1075 | + } | |
1076 | + wordIdx++ | |
1077 | + } | |
1078 | + return x, nil | |
1079 | +} | |
1080 | + | |
1081 | +// FromFloat64 creates a decimal from float64 value. | |
1082 | +func (d *MyDecimal) FromFloat64(f float64) error { | |
1083 | + s := strconv.FormatFloat(f, 'g', -1, 64) | |
1084 | + return d.FromString([]byte(s)) | |
1085 | +} | |
1086 | + | |
1087 | +// ToFloat64 converts decimal to float64 value. | |
1088 | +func (d *MyDecimal) ToFloat64() (float64, error) { | |
1089 | + f, err := strconv.ParseFloat(d.String(), 64) | |
1090 | + if err != nil { | |
1091 | + err = ErrOverflow | |
1092 | + } | |
1093 | + return f, err | |
1094 | +} | |
1095 | + | |
1096 | +/* | |
1097 | +ToBin converts decimal to its binary fixed-length representation | |
1098 | +two representations of the same length can be compared with memcmp | |
1099 | +with the correct -1/0/+1 result | |
1100 | + | |
1101 | + PARAMS | |
1102 | + precision/frac - if precision is 0, internal value of the decimal will be used, | |
1103 | + then the encoded value is not memory comparable. | |
1104 | + | |
1105 | + NOTE | |
1106 | + the buffer is assumed to be of the size DecimalBinSize(precision, frac) | |
1107 | + | |
1108 | + RETURN VALUE | |
1109 | + bin - binary value | |
1110 | + errCode - eDecOK/eDecTruncate/eDecOverflow | |
1111 | + | |
1112 | + DESCRIPTION | |
1113 | + for storage decimal numbers are converted to the "binary" format. | |
1114 | + | |
1115 | + This format has the following properties: | |
1116 | + 1. length of the binary representation depends on the {precision, frac} | |
1117 | + as provided by the caller and NOT on the digitsInt/digitsFrac of the decimal to | |
1118 | + convert. | |
1119 | + 2. binary representations of the same {precision, frac} can be compared | |
1120 | + with memcmp - with the same result as DecimalCompare() of the original | |
1121 | + decimals (not taking into account possible precision loss during | |
1122 | + conversion). | |
1123 | + | |
1124 | + This binary format is as follows: | |
1125 | + 1. First the number is converted to have a requested precision and frac. | |
1126 | + 2. Every full digitsPerWord digits of digitsInt part are stored in 4 bytes | |
1127 | + as is | |
1128 | + 3. The first digitsInt % digitesPerWord digits are stored in the reduced | |
1129 | + number of bytes (enough bytes to store this number of digits - | |
1130 | + see dig2bytes) | |
1131 | + 4. same for frac - full word are stored as is, | |
1132 | + the last frac % digitsPerWord digits - in the reduced number of bytes. | |
1133 | + 5. If the number is negative - every byte is inversed. | |
1134 | + 5. The very first bit of the resulting byte array is inverted (because | |
1135 | + memcmp compares unsigned bytes, see property 2 above) | |
1136 | + | |
1137 | + Example: | |
1138 | + | |
1139 | + 1234567890.1234 | |
1140 | + | |
1141 | + internally is represented as 3 words | |
1142 | + | |
1143 | + 1 234567890 123400000 | |
1144 | + | |
1145 | + (assuming we want a binary representation with precision=14, frac=4) | |
1146 | + in hex it's | |
1147 | + | |
1148 | + 00-00-00-01 0D-FB-38-D2 07-5A-EF-40 | |
1149 | + | |
1150 | + now, middle word is full - it stores 9 decimal digits. It goes | |
1151 | + into binary representation as is: | |
1152 | + | |
1153 | + | |
1154 | + ........... 0D-FB-38-D2 ............ | |
1155 | + | |
1156 | + First word has only one decimal digit. We can store one digit in | |
1157 | + one byte, no need to waste four: | |
1158 | + | |
1159 | + 01 0D-FB-38-D2 ............ | |
1160 | + | |
1161 | + now, last word. It's 123400000. We can store 1234 in two bytes: | |
1162 | + | |
1163 | + 01 0D-FB-38-D2 04-D2 | |
1164 | + | |
1165 | + So, we've packed 12 bytes number in 7 bytes. | |
1166 | + And now we invert the highest bit to get the final result: | |
1167 | + | |
1168 | + 81 0D FB 38 D2 04 D2 | |
1169 | + | |
1170 | + And for -1234567890.1234 it would be | |
1171 | + | |
1172 | + 7E F2 04 C7 2D FB 2D | |
1173 | +*/ | |
1174 | +func (d *MyDecimal) ToBin(precision, frac int) ([]byte, error) { | |
1175 | + if precision > digitsPerWord*maxWordBufLen || precision < 0 || frac > MaxDecimalScale || frac < 0 { | |
1176 | + return nil, ErrBadNumber | |
1177 | + } | |
1178 | + var err error | |
1179 | + var mask int32 | |
1180 | + if d.negative { | |
1181 | + mask = -1 | |
1182 | + } | |
1183 | + digitsInt := precision - frac | |
1184 | + wordsInt := digitsInt / digitsPerWord | |
1185 | + leadingDigits := digitsInt - wordsInt*digitsPerWord | |
1186 | + wordsFrac := frac / digitsPerWord | |
1187 | + trailingDigits := frac - wordsFrac*digitsPerWord | |
1188 | + | |
1189 | + wordsFracFrom := int(d.digitsFrac) / digitsPerWord | |
1190 | + trailingDigitsFrom := int(d.digitsFrac) - wordsFracFrom*digitsPerWord | |
1191 | + intSize := wordsInt*wordSize + dig2bytes[leadingDigits] | |
1192 | + fracSize := wordsFrac*wordSize + dig2bytes[trailingDigits] | |
1193 | + fracSizeFrom := wordsFracFrom*wordSize + dig2bytes[trailingDigitsFrom] | |
1194 | + originIntSize := intSize | |
1195 | + originFracSize := fracSize | |
1196 | + bin := make([]byte, intSize+fracSize) | |
1197 | + binIdx := 0 | |
1198 | + wordIdxFrom, digitsIntFrom := d.removeLeadingZeros() | |
1199 | + if digitsIntFrom+fracSizeFrom == 0 { | |
1200 | + mask = 0 | |
1201 | + digitsInt = 1 | |
1202 | + } | |
1203 | + | |
1204 | + wordsIntFrom := digitsIntFrom / digitsPerWord | |
1205 | + leadingDigitsFrom := digitsIntFrom - wordsIntFrom*digitsPerWord | |
1206 | + iSizeFrom := wordsIntFrom*wordSize + dig2bytes[leadingDigitsFrom] | |
1207 | + | |
1208 | + if digitsInt < digitsIntFrom { | |
1209 | + wordIdxFrom += wordsIntFrom - wordsInt | |
1210 | + if leadingDigitsFrom > 0 { | |
1211 | + wordIdxFrom++ | |
1212 | + } | |
1213 | + if leadingDigits > 0 { | |
1214 | + wordIdxFrom-- | |
1215 | + } | |
1216 | + wordsIntFrom = wordsInt | |
1217 | + leadingDigitsFrom = leadingDigits | |
1218 | + err = ErrOverflow | |
1219 | + } else if intSize > iSizeFrom { | |
1220 | + for intSize > iSizeFrom { | |
1221 | + intSize-- | |
1222 | + bin[binIdx] = byte(mask) | |
1223 | + binIdx++ | |
1224 | + } | |
1225 | + } | |
1226 | + | |
1227 | + if fracSize < fracSizeFrom { | |
1228 | + wordsFracFrom = wordsFrac | |
1229 | + trailingDigitsFrom = trailingDigits | |
1230 | + err = ErrTruncated | |
1231 | + } else if fracSize > fracSizeFrom && trailingDigitsFrom > 0 { | |
1232 | + if wordsFrac == wordsFracFrom { | |
1233 | + trailingDigitsFrom = trailingDigits | |
1234 | + fracSize = fracSizeFrom | |
1235 | + } else { | |
1236 | + wordsFracFrom++ | |
1237 | + trailingDigitsFrom = 0 | |
1238 | + } | |
1239 | + } | |
1240 | + // xIntFrom part | |
1241 | + if leadingDigitsFrom > 0 { | |
1242 | + i := dig2bytes[leadingDigitsFrom] | |
1243 | + x := (d.wordBuf[wordIdxFrom] % powers10[leadingDigitsFrom]) ^ mask | |
1244 | + wordIdxFrom++ | |
1245 | + writeWord(bin[binIdx:], x, i) | |
1246 | + binIdx += i | |
1247 | + } | |
1248 | + | |
1249 | + // wordsInt + wordsFrac part. | |
1250 | + for stop := wordIdxFrom + wordsIntFrom + wordsFracFrom; wordIdxFrom < stop; binIdx += wordSize { | |
1251 | + x := d.wordBuf[wordIdxFrom] ^ mask | |
1252 | + wordIdxFrom++ | |
1253 | + writeWord(bin[binIdx:], x, 4) | |
1254 | + } | |
1255 | + | |
1256 | + // xFracFrom part | |
1257 | + if trailingDigitsFrom > 0 { | |
1258 | + var x int32 | |
1259 | + i := dig2bytes[trailingDigitsFrom] | |
1260 | + lim := trailingDigits | |
1261 | + if wordsFracFrom < wordsFrac { | |
1262 | + lim = digitsPerWord | |
1263 | + } | |
1264 | + | |
1265 | + for trailingDigitsFrom < lim && dig2bytes[trailingDigitsFrom] == i { | |
1266 | + trailingDigitsFrom++ | |
1267 | + } | |
1268 | + x = (d.wordBuf[wordIdxFrom] / powers10[digitsPerWord-trailingDigitsFrom]) ^ mask | |
1269 | + writeWord(bin[binIdx:], x, i) | |
1270 | + binIdx += i | |
1271 | + } | |
1272 | + if fracSize > fracSizeFrom { | |
1273 | + binIdxEnd := originIntSize + originFracSize | |
1274 | + for fracSize > fracSizeFrom && binIdx < binIdxEnd { | |
1275 | + fracSize-- | |
1276 | + bin[binIdx] = byte(mask) | |
1277 | + binIdx++ | |
1278 | + } | |
1279 | + } | |
1280 | + bin[0] ^= 0x80 | |
1281 | + return bin, err | |
1282 | +} | |
1283 | + | |
1284 | +// ToHashKey removes the leading and trailing zeros and generates a hash key. | |
1285 | +// Two Decimals dec0 and dec1 with different fraction will generate the same hash keys if dec0.Compare(dec1) == 0. | |
1286 | +func (d *MyDecimal) ToHashKey() ([]byte, error) { | |
1287 | + _, digitsInt := d.removeLeadingZeros() | |
1288 | + _, digitsFrac := d.removeTrailingZeros() | |
1289 | + prec := digitsInt + digitsFrac | |
1290 | + if prec == 0 { // zeroDecimal | |
1291 | + prec = 1 | |
1292 | + } | |
1293 | + buf, err := d.ToBin(prec, digitsFrac) | |
1294 | + if err == ErrTruncated { | |
1295 | + // This err is caused by shorter digitsFrac; | |
1296 | + // After removing the trailing zeros from a Decimal, | |
1297 | + // so digitsFrac may be less than the real digitsFrac of the Decimal, | |
1298 | + // thus ErrTruncated may be raised, we can ignore it here. | |
1299 | + err = nil | |
1300 | + } | |
1301 | + return buf, err | |
1302 | +} | |
1303 | + | |
1304 | +// PrecisionAndFrac returns the internal precision and frac number. | |
1305 | +func (d *MyDecimal) PrecisionAndFrac() (precision, frac int) { | |
1306 | + frac = int(d.digitsFrac) | |
1307 | + _, digitsInt := d.removeLeadingZeros() | |
1308 | + precision = digitsInt + frac | |
1309 | + if precision == 0 { | |
1310 | + precision = 1 | |
1311 | + } | |
1312 | + return | |
1313 | +} | |
1314 | + | |
1315 | +// IsZero checks whether it's a zero decimal. | |
1316 | +func (d *MyDecimal) IsZero() bool { | |
1317 | + isZero := true | |
1318 | + for _, val := range d.wordBuf { | |
1319 | + if val != 0 { | |
1320 | + isZero = false | |
1321 | + break | |
1322 | + } | |
1323 | + } | |
1324 | + return isZero | |
1325 | +} | |
1326 | + | |
1327 | +// FromBin Restores decimal from its binary fixed-length representation. | |
1328 | +func (d *MyDecimal) FromBin(bin []byte, precision, frac int) (binSize int, err error) { | |
1329 | + if len(bin) == 0 { | |
1330 | + *d = zeroMyDecimal | |
1331 | + return 0, ErrBadNumber | |
1332 | + } | |
1333 | + digitsInt := precision - frac | |
1334 | + wordsInt := digitsInt / digitsPerWord | |
1335 | + leadingDigits := digitsInt - wordsInt*digitsPerWord | |
1336 | + wordsFrac := frac / digitsPerWord | |
1337 | + trailingDigits := frac - wordsFrac*digitsPerWord | |
1338 | + wordsIntTo := wordsInt | |
1339 | + if leadingDigits > 0 { | |
1340 | + wordsIntTo++ | |
1341 | + } | |
1342 | + wordsFracTo := wordsFrac | |
1343 | + if trailingDigits > 0 { | |
1344 | + wordsFracTo++ | |
1345 | + } | |
1346 | + | |
1347 | + binIdx := 0 | |
1348 | + mask := int32(-1) | |
1349 | + if bin[binIdx]&0x80 > 0 { | |
1350 | + mask = 0 | |
1351 | + } | |
1352 | + binSize = DecimalBinSize(precision, frac) | |
1353 | + dCopy := make([]byte, 40) | |
1354 | + dCopy = dCopy[:binSize] | |
1355 | + copy(dCopy, bin) | |
1356 | + dCopy[0] ^= 0x80 | |
1357 | + bin = dCopy | |
1358 | + oldWordsIntTo := wordsIntTo | |
1359 | + wordsIntTo, wordsFracTo, err = fixWordCntError(wordsIntTo, wordsFracTo) | |
1360 | + if err != nil { | |
1361 | + if wordsIntTo < oldWordsIntTo { | |
1362 | + binIdx += dig2bytes[leadingDigits] + (wordsInt-wordsIntTo)*wordSize | |
1363 | + } else { | |
1364 | + trailingDigits = 0 | |
1365 | + wordsFrac = wordsFracTo | |
1366 | + } | |
1367 | + } | |
1368 | + d.negative = mask != 0 | |
1369 | + d.digitsInt = int8(wordsInt*digitsPerWord + leadingDigits) | |
1370 | + d.digitsFrac = int8(wordsFrac*digitsPerWord + trailingDigits) | |
1371 | + | |
1372 | + wordIdx := 0 | |
1373 | + if leadingDigits > 0 { | |
1374 | + i := dig2bytes[leadingDigits] | |
1375 | + x := readWord(bin[binIdx:], i) | |
1376 | + binIdx += i | |
1377 | + d.wordBuf[wordIdx] = x ^ mask | |
1378 | + if uint64(d.wordBuf[wordIdx]) >= uint64(powers10[leadingDigits+1]) { | |
1379 | + *d = zeroMyDecimal | |
1380 | + return binSize, ErrBadNumber | |
1381 | + } | |
1382 | + if wordIdx > 0 || d.wordBuf[wordIdx] != 0 { | |
1383 | + wordIdx++ | |
1384 | + } else { | |
1385 | + d.digitsInt -= int8(leadingDigits) | |
1386 | + } | |
1387 | + } | |
1388 | + for stop := binIdx + wordsInt*wordSize; binIdx < stop; binIdx += wordSize { | |
1389 | + d.wordBuf[wordIdx] = readWord(bin[binIdx:], 4) ^ mask | |
1390 | + if uint32(d.wordBuf[wordIdx]) > wordMax { | |
1391 | + *d = zeroMyDecimal | |
1392 | + return binSize, ErrBadNumber | |
1393 | + } | |
1394 | + if wordIdx > 0 || d.wordBuf[wordIdx] != 0 { | |
1395 | + wordIdx++ | |
1396 | + } else { | |
1397 | + d.digitsInt -= digitsPerWord | |
1398 | + } | |
1399 | + } | |
1400 | + | |
1401 | + for stop := binIdx + wordsFrac*wordSize; binIdx < stop; binIdx += wordSize { | |
1402 | + d.wordBuf[wordIdx] = readWord(bin[binIdx:], 4) ^ mask | |
1403 | + if uint32(d.wordBuf[wordIdx]) > wordMax { | |
1404 | + *d = zeroMyDecimal | |
1405 | + return binSize, ErrBadNumber | |
1406 | + } | |
1407 | + wordIdx++ | |
1408 | + } | |
1409 | + | |
1410 | + if trailingDigits > 0 { | |
1411 | + i := dig2bytes[trailingDigits] | |
1412 | + x := readWord(bin[binIdx:], i) | |
1413 | + d.wordBuf[wordIdx] = (x ^ mask) * powers10[digitsPerWord-trailingDigits] | |
1414 | + if uint32(d.wordBuf[wordIdx]) > wordMax { | |
1415 | + *d = zeroMyDecimal | |
1416 | + return binSize, ErrBadNumber | |
1417 | + } | |
1418 | + } | |
1419 | + | |
1420 | + if d.digitsInt == 0 && d.digitsFrac == 0 { | |
1421 | + *d = zeroMyDecimal | |
1422 | + } | |
1423 | + d.resultFrac = int8(frac) | |
1424 | + return binSize, err | |
1425 | +} | |
1426 | + | |
1427 | +// DecimalBinSize returns the size of array to hold a binary representation of a decimal. | |
1428 | +func DecimalBinSize(precision, frac int) int { | |
1429 | + digitsInt := precision - frac | |
1430 | + wordsInt := digitsInt / digitsPerWord | |
1431 | + wordsFrac := frac / digitsPerWord | |
1432 | + xInt := digitsInt - wordsInt*digitsPerWord | |
1433 | + xFrac := frac - wordsFrac*digitsPerWord | |
1434 | + return wordsInt*wordSize + dig2bytes[xInt] + wordsFrac*wordSize + dig2bytes[xFrac] | |
1435 | +} | |
1436 | + | |
1437 | +func readWord(b []byte, size int) int32 { | |
1438 | + var x int32 | |
1439 | + switch size { | |
1440 | + case 1: | |
1441 | + x = int32(int8(b[0])) | |
1442 | + case 2: | |
1443 | + x = int32(int8(b[0]))<<8 + int32(b[1]) | |
1444 | + case 3: | |
1445 | + if b[0]&128 > 0 { | |
1446 | + x = int32(uint32(255)<<24 | uint32(b[0])<<16 | uint32(b[1])<<8 | uint32(b[2])) | |
1447 | + } else { | |
1448 | + x = int32(uint32(b[0])<<16 | uint32(b[1])<<8 | uint32(b[2])) | |
1449 | + } | |
1450 | + case 4: | |
1451 | + x = int32(b[3]) + int32(b[2])<<8 + int32(b[1])<<16 + int32(int8(b[0]))<<24 | |
1452 | + } | |
1453 | + return x | |
1454 | +} | |
1455 | + | |
1456 | +func writeWord(b []byte, word int32, size int) { | |
1457 | + v := uint32(word) | |
1458 | + switch size { | |
1459 | + case 1: | |
1460 | + b[0] = byte(word) | |
1461 | + case 2: | |
1462 | + b[0] = byte(v >> 8) | |
1463 | + b[1] = byte(v) | |
1464 | + case 3: | |
1465 | + b[0] = byte(v >> 16) | |
1466 | + b[1] = byte(v >> 8) | |
1467 | + b[2] = byte(v) | |
1468 | + case 4: | |
1469 | + b[0] = byte(v >> 24) | |
1470 | + b[1] = byte(v >> 16) | |
1471 | + b[2] = byte(v >> 8) | |
1472 | + b[3] = byte(v) | |
1473 | + } | |
1474 | +} | |
1475 | + | |
1476 | +// Compare compares one decimal to another, returns -1/0/1. | |
1477 | +func (d *MyDecimal) Compare(to *MyDecimal) int { | |
1478 | + if d.negative == to.negative { | |
1479 | + cmp, err := doSub(d, to, nil) | |
1480 | + Log(err) | |
1481 | + return cmp | |
1482 | + } | |
1483 | + if d.negative { | |
1484 | + return -1 | |
1485 | + } | |
1486 | + return 1 | |
1487 | +} | |
1488 | + | |
1489 | +// DecimalNeg reverses decimal's sign. | |
1490 | +func DecimalNeg(from *MyDecimal) *MyDecimal { | |
1491 | + to := *from | |
1492 | + if from.IsZero() { | |
1493 | + return &to | |
1494 | + } | |
1495 | + to.negative = !from.negative | |
1496 | + return &to | |
1497 | +} | |
1498 | + | |
1499 | +// DecimalAdd adds two decimals, sets the result to 'to'. | |
1500 | +// Note: DO NOT use `from1` or `from2` as `to` since the metadata | |
1501 | +// of `to` may be changed during evaluating. | |
1502 | +func DecimalAdd(from1, from2, to *MyDecimal) error { | |
1503 | + from1, from2, to = validateArgs(from1, from2, to) | |
1504 | + to.resultFrac = myMaxInt8(from1.resultFrac, from2.resultFrac) | |
1505 | + if from1.negative == from2.negative { | |
1506 | + return doAdd(from1, from2, to) | |
1507 | + } | |
1508 | + _, err := doSub(from1, from2, to) | |
1509 | + return err | |
1510 | +} | |
1511 | + | |
1512 | +// DecimalSub subs one decimal from another, sets the result to 'to'. | |
1513 | +func DecimalSub(from1, from2, to *MyDecimal) error { | |
1514 | + from1, from2, to = validateArgs(from1, from2, to) | |
1515 | + to.resultFrac = myMaxInt8(from1.resultFrac, from2.resultFrac) | |
1516 | + if from1.negative == from2.negative { | |
1517 | + _, err := doSub(from1, from2, to) | |
1518 | + return err | |
1519 | + } | |
1520 | + return doAdd(from1, from2, to) | |
1521 | +} | |
1522 | + | |
1523 | +func validateArgs(f1, f2, to *MyDecimal) (*MyDecimal, *MyDecimal, *MyDecimal) { | |
1524 | + if to == nil { | |
1525 | + return f1, f2, to | |
1526 | + } | |
1527 | + if f1 == to { | |
1528 | + tmp := *f1 | |
1529 | + f1 = &tmp | |
1530 | + } | |
1531 | + if f2 == to { | |
1532 | + tmp := *f2 | |
1533 | + f2 = &tmp | |
1534 | + } | |
1535 | + to.digitsFrac = 0 | |
1536 | + to.digitsInt = 0 | |
1537 | + to.resultFrac = 0 | |
1538 | + to.negative = false | |
1539 | + for i := range to.wordBuf { | |
1540 | + to.wordBuf[i] = 0 | |
1541 | + } | |
1542 | + return f1, f2, to | |
1543 | +} | |
1544 | + | |
1545 | +func doSub(from1, from2, to *MyDecimal) (cmp int, err error) { | |
1546 | + var ( | |
1547 | + wordsInt1 = digitsToWords(int(from1.digitsInt)) | |
1548 | + wordsFrac1 = digitsToWords(int(from1.digitsFrac)) | |
1549 | + wordsInt2 = digitsToWords(int(from2.digitsInt)) | |
1550 | + wordsFrac2 = digitsToWords(int(from2.digitsFrac)) | |
1551 | + wordsFracTo = myMax(wordsFrac1, wordsFrac2) | |
1552 | + | |
1553 | + start1 = 0 | |
1554 | + stop1 = wordsInt1 | |
1555 | + idx1 = 0 | |
1556 | + start2 = 0 | |
1557 | + stop2 = wordsInt2 | |
1558 | + idx2 = 0 | |
1559 | + ) | |
1560 | + if from1.wordBuf[idx1] == 0 { | |
1561 | + for idx1 < stop1 && from1.wordBuf[idx1] == 0 { | |
1562 | + idx1++ | |
1563 | + } | |
1564 | + start1 = idx1 | |
1565 | + wordsInt1 = stop1 - idx1 | |
1566 | + } | |
1567 | + if from2.wordBuf[idx2] == 0 { | |
1568 | + for idx2 < stop2 && from2.wordBuf[idx2] == 0 { | |
1569 | + idx2++ | |
1570 | + } | |
1571 | + start2 = idx2 | |
1572 | + wordsInt2 = stop2 - idx2 | |
1573 | + } | |
1574 | + | |
1575 | + var carry int32 | |
1576 | + if wordsInt2 > wordsInt1 { | |
1577 | + carry = 1 | |
1578 | + } else if wordsInt2 == wordsInt1 { | |
1579 | + end1 := stop1 + wordsFrac1 - 1 | |
1580 | + end2 := stop2 + wordsFrac2 - 1 | |
1581 | + for idx1 <= end1 && from1.wordBuf[end1] == 0 { | |
1582 | + end1-- | |
1583 | + } | |
1584 | + for idx2 <= end2 && from2.wordBuf[end2] == 0 { | |
1585 | + end2-- | |
1586 | + } | |
1587 | + wordsFrac1 = end1 - stop1 + 1 | |
1588 | + wordsFrac2 = end2 - stop2 + 1 | |
1589 | + for idx1 <= end1 && idx2 <= end2 && from1.wordBuf[idx1] == from2.wordBuf[idx2] { | |
1590 | + idx1++ | |
1591 | + idx2++ | |
1592 | + } | |
1593 | + if idx1 <= end1 { | |
1594 | + if idx2 <= end2 && from2.wordBuf[idx2] > from1.wordBuf[idx1] { | |
1595 | + carry = 1 | |
1596 | + } else { | |
1597 | + carry = 0 | |
1598 | + } | |
1599 | + } else { | |
1600 | + if idx2 <= end2 { | |
1601 | + carry = 1 | |
1602 | + } else { | |
1603 | + if to == nil { | |
1604 | + return 0, nil | |
1605 | + } | |
1606 | + *to = zeroMyDecimalWithFrac(to.resultFrac) | |
1607 | + return 0, nil | |
1608 | + } | |
1609 | + } | |
1610 | + } | |
1611 | + | |
1612 | + if to == nil { | |
1613 | + if carry > 0 == from1.negative { // from2 is negative too. | |
1614 | + return 1, nil | |
1615 | + } | |
1616 | + return -1, nil | |
1617 | + } | |
1618 | + | |
1619 | + to.negative = from1.negative | |
1620 | + | |
1621 | + /* ensure that always idx1 > idx2 (and wordsInt1 >= wordsInt2) */ | |
1622 | + if carry > 0 { | |
1623 | + from1, from2 = from2, from1 | |
1624 | + start1, start2 = start2, start1 | |
1625 | + wordsInt1, wordsInt2 = wordsInt2, wordsInt1 | |
1626 | + wordsFrac1, wordsFrac2 = wordsFrac2, wordsFrac1 | |
1627 | + to.negative = !to.negative | |
1628 | + } | |
1629 | + | |
1630 | + wordsInt1, wordsFracTo, err = fixWordCntError(wordsInt1, wordsFracTo) | |
1631 | + idxTo := wordsInt1 + wordsFracTo | |
1632 | + to.digitsFrac = from1.digitsFrac | |
1633 | + if to.digitsFrac < from2.digitsFrac { | |
1634 | + to.digitsFrac = from2.digitsFrac | |
1635 | + } | |
1636 | + to.digitsInt = int8(wordsInt1 * digitsPerWord) | |
1637 | + if err != nil { | |
1638 | + if to.digitsFrac > int8(wordsFracTo*digitsPerWord) { | |
1639 | + to.digitsFrac = int8(wordsFracTo * digitsPerWord) | |
1640 | + } | |
1641 | + if wordsFrac1 > wordsFracTo { | |
1642 | + wordsFrac1 = wordsFracTo | |
1643 | + } | |
1644 | + if wordsFrac2 > wordsFracTo { | |
1645 | + wordsFrac2 = wordsFracTo | |
1646 | + } | |
1647 | + if wordsInt2 > wordsInt1 { | |
1648 | + wordsInt2 = wordsInt1 | |
1649 | + } | |
1650 | + } | |
1651 | + carry = 0 | |
1652 | + | |
1653 | + /* part 1 - max(frac) ... min (frac) */ | |
1654 | + if wordsFrac1 > wordsFrac2 { | |
1655 | + idx1 = start1 + wordsInt1 + wordsFrac1 | |
1656 | + stop1 = start1 + wordsInt1 + wordsFrac2 | |
1657 | + idx2 = start2 + wordsInt2 + wordsFrac2 | |
1658 | + for wordsFracTo > wordsFrac1 { | |
1659 | + wordsFracTo-- | |
1660 | + idxTo-- | |
1661 | + to.wordBuf[idxTo] = 0 | |
1662 | + } | |
1663 | + for idx1 > stop1 { | |
1664 | + idxTo-- | |
1665 | + idx1-- | |
1666 | + to.wordBuf[idxTo] = from1.wordBuf[idx1] | |
1667 | + } | |
1668 | + } else { | |
1669 | + idx1 = start1 + wordsInt1 + wordsFrac1 | |
1670 | + idx2 = start2 + wordsInt2 + wordsFrac2 | |
1671 | + stop2 = start2 + wordsInt2 + wordsFrac1 | |
1672 | + for wordsFracTo > wordsFrac2 { | |
1673 | + wordsFracTo-- | |
1674 | + idxTo-- | |
1675 | + to.wordBuf[idxTo] = 0 | |
1676 | + } | |
1677 | + for idx2 > stop2 { | |
1678 | + idxTo-- | |
1679 | + idx2-- | |
1680 | + to.wordBuf[idxTo], carry = sub(0, from2.wordBuf[idx2], carry) | |
1681 | + } | |
1682 | + } | |
1683 | + | |
1684 | + /* part 2 - min(frac) ... wordsInt2 */ | |
1685 | + for idx2 > start2 { | |
1686 | + idxTo-- | |
1687 | + idx1-- | |
1688 | + idx2-- | |
1689 | + to.wordBuf[idxTo], carry = sub(from1.wordBuf[idx1], from2.wordBuf[idx2], carry) | |
1690 | + } | |
1691 | + | |
1692 | + /* part 3 - wordsInt2 ... wordsInt1 */ | |
1693 | + for carry > 0 && idx1 > start1 { | |
1694 | + idxTo-- | |
1695 | + idx1-- | |
1696 | + to.wordBuf[idxTo], carry = sub(from1.wordBuf[idx1], 0, carry) | |
1697 | + } | |
1698 | + for idx1 > start1 { | |
1699 | + idxTo-- | |
1700 | + idx1-- | |
1701 | + to.wordBuf[idxTo] = from1.wordBuf[idx1] | |
1702 | + } | |
1703 | + for idxTo > 0 { | |
1704 | + idxTo-- | |
1705 | + to.wordBuf[idxTo] = 0 | |
1706 | + } | |
1707 | + return 0, err | |
1708 | +} | |
1709 | + | |
1710 | +func doAdd(from1, from2, to *MyDecimal) error { | |
1711 | + var ( | |
1712 | + err error | |
1713 | + wordsInt1 = digitsToWords(int(from1.digitsInt)) | |
1714 | + wordsFrac1 = digitsToWords(int(from1.digitsFrac)) | |
1715 | + wordsInt2 = digitsToWords(int(from2.digitsInt)) | |
1716 | + wordsFrac2 = digitsToWords(int(from2.digitsFrac)) | |
1717 | + wordsIntTo = myMax(wordsInt1, wordsInt2) | |
1718 | + wordsFracTo = myMax(wordsFrac1, wordsFrac2) | |
1719 | + ) | |
1720 | + | |
1721 | + var x int32 | |
1722 | + if wordsInt1 > wordsInt2 { | |
1723 | + x = from1.wordBuf[0] | |
1724 | + } else if wordsInt2 > wordsInt1 { | |
1725 | + x = from2.wordBuf[0] | |
1726 | + } else { | |
1727 | + x = from1.wordBuf[0] + from2.wordBuf[0] | |
1728 | + } | |
1729 | + if x > wordMax-1 { /* yes, there is */ | |
1730 | + wordsIntTo++ | |
1731 | + to.wordBuf[0] = 0 /* safety */ | |
1732 | + } | |
1733 | + | |
1734 | + wordsIntTo, wordsFracTo, err = fixWordCntError(wordsIntTo, wordsFracTo) | |
1735 | + if err == ErrOverflow { | |
1736 | + maxDecimal(wordBufLen*digitsPerWord, 0, to) | |
1737 | + return err | |
1738 | + } | |
1739 | + idxTo := wordsIntTo + wordsFracTo | |
1740 | + to.negative = from1.negative | |
1741 | + to.digitsInt = int8(wordsIntTo * digitsPerWord) | |
1742 | + to.digitsFrac = myMaxInt8(from1.digitsFrac, from2.digitsFrac) | |
1743 | + | |
1744 | + if err != nil { | |
1745 | + if to.digitsFrac > int8(wordsFracTo*digitsPerWord) { | |
1746 | + to.digitsFrac = int8(wordsFracTo * digitsPerWord) | |
1747 | + } | |
1748 | + if wordsFrac1 > wordsFracTo { | |
1749 | + wordsFrac1 = wordsFracTo | |
1750 | + } | |
1751 | + if wordsFrac2 > wordsFracTo { | |
1752 | + wordsFrac2 = wordsFracTo | |
1753 | + } | |
1754 | + if wordsInt1 > wordsIntTo { | |
1755 | + wordsInt1 = wordsIntTo | |
1756 | + } | |
1757 | + if wordsInt2 > wordsIntTo { | |
1758 | + wordsInt2 = wordsIntTo | |
1759 | + } | |
1760 | + } | |
1761 | + var dec1, dec2 = from1, from2 | |
1762 | + var idx1, idx2, stop, stop2 int | |
1763 | + /* part 1 - max(frac) ... min (frac) */ | |
1764 | + if wordsFrac1 > wordsFrac2 { | |
1765 | + idx1 = wordsInt1 + wordsFrac1 | |
1766 | + stop = wordsInt1 + wordsFrac2 | |
1767 | + idx2 = wordsInt2 + wordsFrac2 | |
1768 | + if wordsInt1 > wordsInt2 { | |
1769 | + stop2 = wordsInt1 - wordsInt2 | |
1770 | + } | |
1771 | + } else { | |
1772 | + idx1 = wordsInt2 + wordsFrac2 | |
1773 | + stop = wordsInt2 + wordsFrac1 | |
1774 | + idx2 = wordsInt1 + wordsFrac1 | |
1775 | + if wordsInt2 > wordsInt1 { | |
1776 | + stop2 = wordsInt2 - wordsInt1 | |
1777 | + } | |
1778 | + dec1, dec2 = from2, from1 | |
1779 | + } | |
1780 | + for idx1 > stop { | |
1781 | + idxTo-- | |
1782 | + idx1-- | |
1783 | + to.wordBuf[idxTo] = dec1.wordBuf[idx1] | |
1784 | + } | |
1785 | + | |
1786 | + /* part 2 - min(frac) ... min(digitsInt) */ | |
1787 | + carry := int32(0) | |
1788 | + for idx1 > stop2 { | |
1789 | + idx1-- | |
1790 | + idx2-- | |
1791 | + idxTo-- | |
1792 | + to.wordBuf[idxTo], carry = add(dec1.wordBuf[idx1], dec2.wordBuf[idx2], carry) | |
1793 | + } | |
1794 | + | |
1795 | + /* part 3 - min(digitsInt) ... max(digitsInt) */ | |
1796 | + stop = 0 | |
1797 | + if wordsInt1 > wordsInt2 { | |
1798 | + idx1 = wordsInt1 - wordsInt2 | |
1799 | + dec1 = from1 | |
1800 | + } else { | |
1801 | + idx1 = wordsInt2 - wordsInt1 | |
1802 | + dec1 = from2 | |
1803 | + } | |
1804 | + for idx1 > stop { | |
1805 | + idxTo-- | |
1806 | + idx1-- | |
1807 | + to.wordBuf[idxTo], carry = add(dec1.wordBuf[idx1], 0, carry) | |
1808 | + } | |
1809 | + if carry > 0 { | |
1810 | + idxTo-- | |
1811 | + to.wordBuf[idxTo] = 1 | |
1812 | + } | |
1813 | + return err | |
1814 | +} | |
1815 | + | |
1816 | +func maxDecimal(precision, frac int, to *MyDecimal) { | |
1817 | + digitsInt := precision - frac | |
1818 | + to.negative = false | |
1819 | + to.digitsInt = int8(digitsInt) | |
1820 | + idx := 0 | |
1821 | + if digitsInt > 0 { | |
1822 | + firstWordDigits := digitsInt % digitsPerWord | |
1823 | + if firstWordDigits > 0 { | |
1824 | + to.wordBuf[idx] = powers10[firstWordDigits] - 1 /* get 9 99 999 ... */ | |
1825 | + idx++ | |
1826 | + } | |
1827 | + for digitsInt /= digitsPerWord; digitsInt > 0; digitsInt-- { | |
1828 | + to.wordBuf[idx] = wordMax | |
1829 | + idx++ | |
1830 | + } | |
1831 | + } | |
1832 | + to.digitsFrac = int8(frac) | |
1833 | + if frac > 0 { | |
1834 | + lastDigits := frac % digitsPerWord | |
1835 | + for frac /= digitsPerWord; frac > 0; frac-- { | |
1836 | + to.wordBuf[idx] = wordMax | |
1837 | + idx++ | |
1838 | + } | |
1839 | + if lastDigits > 0 { | |
1840 | + to.wordBuf[idx] = fracMax[lastDigits-1] | |
1841 | + } | |
1842 | + } | |
1843 | +} | |
1844 | + | |
1845 | +/* | |
1846 | +DecimalMul multiplies two decimals. | |
1847 | + | |
1848 | + from1, from2 - factors | |
1849 | + to - product | |
1850 | + | |
1851 | + RETURN VALUE | |
1852 | + E_DEC_OK/E_DEC_TRUNCATED/E_DEC_OVERFLOW; | |
1853 | + | |
1854 | + NOTES | |
1855 | + in this implementation, with wordSize=4 we have digitsPerWord=9, | |
1856 | + and 63-digit number will take only 7 words (basically a 7-digit | |
1857 | + "base 999999999" number). Thus there's no need in fast multiplication | |
1858 | + algorithms, 7-digit numbers can be multiplied with a naive O(n*n) | |
1859 | + method. | |
1860 | + | |
1861 | + XXX if this library is to be used with huge numbers of thousands of | |
1862 | + digits, fast multiplication must be implemented. | |
1863 | +*/ | |
1864 | +func DecimalMul(from1, from2, to *MyDecimal) error { | |
1865 | + from1, from2, to = validateArgs(from1, from2, to) | |
1866 | + var ( | |
1867 | + err error | |
1868 | + wordsInt1 = digitsToWords(int(from1.digitsInt)) | |
1869 | + wordsFrac1 = digitsToWords(int(from1.digitsFrac)) | |
1870 | + wordsInt2 = digitsToWords(int(from2.digitsInt)) | |
1871 | + wordsFrac2 = digitsToWords(int(from2.digitsFrac)) | |
1872 | + wordsIntTo = digitsToWords(int(from1.digitsInt) + int(from2.digitsInt)) | |
1873 | + wordsFracTo = wordsFrac1 + wordsFrac2 | |
1874 | + idx1 = wordsInt1 | |
1875 | + idx2 = wordsInt2 | |
1876 | + idxTo int | |
1877 | + tmp1 = wordsIntTo | |
1878 | + tmp2 = wordsFracTo | |
1879 | + ) | |
1880 | + to.resultFrac = myMinInt8(from1.resultFrac+from2.resultFrac, MaxDecimalScale) | |
1881 | + wordsIntTo, wordsFracTo, err = fixWordCntError(wordsIntTo, wordsFracTo) | |
1882 | + to.negative = from1.negative != from2.negative | |
1883 | + to.digitsFrac = from1.digitsFrac + from2.digitsFrac | |
1884 | + if to.digitsFrac > notFixedDec { | |
1885 | + to.digitsFrac = notFixedDec | |
1886 | + } | |
1887 | + to.digitsInt = int8(wordsIntTo * digitsPerWord) | |
1888 | + if err == ErrOverflow { | |
1889 | + return err | |
1890 | + } | |
1891 | + if err != nil { | |
1892 | + if to.digitsFrac > int8(wordsFracTo*digitsPerWord) { | |
1893 | + to.digitsFrac = int8(wordsFracTo * digitsPerWord) | |
1894 | + } | |
1895 | + if to.digitsInt > int8(wordsIntTo*digitsPerWord) { | |
1896 | + to.digitsInt = int8(wordsIntTo * digitsPerWord) | |
1897 | + } | |
1898 | + if tmp1 > wordsIntTo { | |
1899 | + tmp1 -= wordsIntTo | |
1900 | + tmp2 = tmp1 >> 1 | |
1901 | + wordsInt2 -= tmp1 - tmp2 | |
1902 | + wordsFrac1 = 0 | |
1903 | + wordsFrac2 = 0 | |
1904 | + } else { | |
1905 | + tmp2 -= wordsFracTo | |
1906 | + tmp1 = tmp2 >> 1 | |
1907 | + if wordsFrac1 <= wordsFrac2 { | |
1908 | + wordsFrac1 -= tmp1 | |
1909 | + wordsFrac2 -= tmp2 - tmp1 | |
1910 | + } else { | |
1911 | + wordsFrac2 -= tmp1 | |
1912 | + wordsFrac1 -= tmp2 - tmp1 | |
1913 | + } | |
1914 | + } | |
1915 | + } | |
1916 | + startTo := wordsIntTo + wordsFracTo - 1 | |
1917 | + start2 := idx2 + wordsFrac2 - 1 | |
1918 | + stop1 := idx1 - wordsInt1 | |
1919 | + stop2 := idx2 - wordsInt2 | |
1920 | + to.wordBuf = zeroMyDecimal.wordBuf | |
1921 | + | |
1922 | + for idx1 += wordsFrac1 - 1; idx1 >= stop1; idx1-- { | |
1923 | + carry := int32(0) | |
1924 | + idxTo = startTo | |
1925 | + idx2 = start2 | |
1926 | + for idx2 >= stop2 { | |
1927 | + var hi, lo int32 | |
1928 | + p := int64(from1.wordBuf[idx1]) * int64(from2.wordBuf[idx2]) | |
1929 | + hi = int32(p / wordBase) | |
1930 | + lo = int32(p - int64(hi)*wordBase) | |
1931 | + to.wordBuf[idxTo], carry = add2(to.wordBuf[idxTo], lo, carry) | |
1932 | + carry += hi | |
1933 | + idx2-- | |
1934 | + idxTo-- | |
1935 | + } | |
1936 | + if carry > 0 { | |
1937 | + if idxTo < 0 { | |
1938 | + return ErrOverflow | |
1939 | + } | |
1940 | + to.wordBuf[idxTo], carry = add2(to.wordBuf[idxTo], 0, carry) | |
1941 | + } | |
1942 | + for idxTo--; carry > 0; idxTo-- { | |
1943 | + if idxTo < 0 { | |
1944 | + return ErrOverflow | |
1945 | + } | |
1946 | + to.wordBuf[idxTo], carry = add(to.wordBuf[idxTo], 0, carry) | |
1947 | + } | |
1948 | + startTo-- | |
1949 | + } | |
1950 | + | |
1951 | + /* Now we have to check for -0.000 case */ | |
1952 | + if to.negative { | |
1953 | + idx := 0 | |
1954 | + end := wordsIntTo + wordsFracTo | |
1955 | + for { | |
1956 | + if to.wordBuf[idx] != 0 { | |
1957 | + break | |
1958 | + } | |
1959 | + idx++ | |
1960 | + /* We got decimal zero */ | |
1961 | + if idx == end { | |
1962 | + *to = zeroMyDecimalWithFrac(to.resultFrac) | |
1963 | + break | |
1964 | + } | |
1965 | + } | |
1966 | + } | |
1967 | + | |
1968 | + idxTo = 0 | |
1969 | + dToMove := wordsIntTo + digitsToWords(int(to.digitsFrac)) | |
1970 | + for to.wordBuf[idxTo] == 0 && to.digitsInt > digitsPerWord { | |
1971 | + idxTo++ | |
1972 | + to.digitsInt -= digitsPerWord | |
1973 | + dToMove-- | |
1974 | + } | |
1975 | + if idxTo > 0 { | |
1976 | + curIdx := 0 | |
1977 | + for dToMove > 0 { | |
1978 | + to.wordBuf[curIdx] = to.wordBuf[idxTo] | |
1979 | + curIdx++ | |
1980 | + idxTo++ | |
1981 | + dToMove-- | |
1982 | + } | |
1983 | + } | |
1984 | + return err | |
1985 | +} | |
1986 | + | |
1987 | +// DecimalDiv does division of two decimals. | |
1988 | +// | |
1989 | +// from1 - dividend | |
1990 | +// from2 - divisor | |
1991 | +// to - quotient | |
1992 | +// fracIncr - increment of fraction | |
1993 | +func DecimalDiv(from1, from2, to *MyDecimal, fracIncr int) error { | |
1994 | + from1, from2, to = validateArgs(from1, from2, to) | |
1995 | + to.resultFrac = myMinInt8(from1.resultFrac+int8(fracIncr), MaxDecimalScale) | |
1996 | + return doDivMod(from1, from2, to, nil, fracIncr) | |
1997 | +} | |
1998 | + | |
1999 | +/* | |
2000 | +DecimalMod does modulus of two decimals. | |
2001 | + | |
2002 | + from1 - dividend | |
2003 | + from2 - divisor | |
2004 | + to - modulus | |
2005 | + | |
2006 | + RETURN VALUE | |
2007 | + E_DEC_OK/E_DEC_TRUNCATED/E_DEC_OVERFLOW/E_DEC_DIV_ZERO; | |
2008 | + | |
2009 | + NOTES | |
2010 | + see do_div_mod() | |
2011 | + | |
2012 | + DESCRIPTION | |
2013 | + the modulus R in R = M mod N | |
2014 | + | |
2015 | + is defined as | |
2016 | + | |
2017 | + 0 <= |R| < |M| | |
2018 | + sign R == sign M | |
2019 | + R = M - k*N, where k is integer | |
2020 | + | |
2021 | + thus, there's no requirement for M or N to be integers | |
2022 | +*/ | |
2023 | +func DecimalMod(from1, from2, to *MyDecimal) error { | |
2024 | + from1, from2, to = validateArgs(from1, from2, to) | |
2025 | + to.resultFrac = myMaxInt8(from1.resultFrac, from2.resultFrac) | |
2026 | + return doDivMod(from1, from2, nil, to, 0) | |
2027 | +} | |
2028 | + | |
2029 | +func doDivMod(from1, from2, to, mod *MyDecimal, fracIncr int) error { | |
2030 | + var ( | |
2031 | + frac1 = digitsToWords(int(from1.digitsFrac)) * digitsPerWord | |
2032 | + prec1 = int(from1.digitsInt) + frac1 | |
2033 | + frac2 = digitsToWords(int(from2.digitsFrac)) * digitsPerWord | |
2034 | + prec2 = int(from2.digitsInt) + frac2 | |
2035 | + ) | |
2036 | + if mod != nil { | |
2037 | + to = mod | |
2038 | + } | |
2039 | + | |
2040 | + /* removing all the leading zeros */ | |
2041 | + i := ((prec2 - 1) % digitsPerWord) + 1 | |
2042 | + idx2 := 0 | |
2043 | + for prec2 > 0 && from2.wordBuf[idx2] == 0 { | |
2044 | + prec2 -= i | |
2045 | + i = digitsPerWord | |
2046 | + idx2++ | |
2047 | + } | |
2048 | + if prec2 <= 0 { | |
2049 | + /* short-circuit everything: from2 == 0 */ | |
2050 | + return ErrDivByZero | |
2051 | + } | |
2052 | + | |
2053 | + prec2 -= countLeadingZeroes((prec2-1)%digitsPerWord, from2.wordBuf[idx2]) | |
2054 | + i = ((prec1 - 1) % digitsPerWord) + 1 | |
2055 | + idx1 := 0 | |
2056 | + for prec1 > 0 && from1.wordBuf[idx1] == 0 { | |
2057 | + prec1 -= i | |
2058 | + i = digitsPerWord | |
2059 | + idx1++ | |
2060 | + } | |
2061 | + if prec1 <= 0 { | |
2062 | + /* short-circuit everything: from1 == 0 */ | |
2063 | + *to = zeroMyDecimalWithFrac(to.resultFrac) | |
2064 | + return nil | |
2065 | + } | |
2066 | + prec1 -= countLeadingZeroes((prec1-1)%digitsPerWord, from1.wordBuf[idx1]) | |
2067 | + | |
2068 | + /* let's fix fracIncr, taking into account frac1,frac2 increase */ | |
2069 | + fracIncr -= frac1 - int(from1.digitsFrac) + frac2 - int(from2.digitsFrac) | |
2070 | + if fracIncr < 0 { | |
2071 | + fracIncr = 0 | |
2072 | + } | |
2073 | + | |
2074 | + digitsIntTo := (prec1 - frac1) - (prec2 - frac2) | |
2075 | + if from1.wordBuf[idx1] >= from2.wordBuf[idx2] { | |
2076 | + digitsIntTo++ | |
2077 | + } | |
2078 | + var wordsIntTo int | |
2079 | + if digitsIntTo < 0 { | |
2080 | + digitsIntTo /= digitsPerWord | |
2081 | + wordsIntTo = 0 | |
2082 | + } else { | |
2083 | + wordsIntTo = digitsToWords(digitsIntTo) | |
2084 | + } | |
2085 | + var wordsFracTo int | |
2086 | + var err error | |
2087 | + if mod != nil { | |
2088 | + // we're calculating N1 % N2. | |
2089 | + // The result will have | |
2090 | + // digitsFrac=max(frac1, frac2), as for subtraction | |
2091 | + // digitsInt=from2.digitsInt | |
2092 | + to.negative = from1.negative | |
2093 | + to.digitsFrac = myMaxInt8(from1.digitsFrac, from2.digitsFrac) | |
2094 | + } else { | |
2095 | + wordsFracTo = digitsToWords(frac1 + frac2 + fracIncr) | |
2096 | + wordsIntTo, wordsFracTo, err = fixWordCntError(wordsIntTo, wordsFracTo) | |
2097 | + to.negative = from1.negative != from2.negative | |
2098 | + to.digitsInt = int8(wordsIntTo * digitsPerWord) | |
2099 | + to.digitsFrac = int8(wordsFracTo * digitsPerWord) | |
2100 | + } | |
2101 | + idxTo := 0 | |
2102 | + stopTo := wordsIntTo + wordsFracTo | |
2103 | + if mod == nil { | |
2104 | + for digitsIntTo < 0 && idxTo < wordBufLen { | |
2105 | + to.wordBuf[idxTo] = 0 | |
2106 | + idxTo++ | |
2107 | + digitsIntTo++ | |
2108 | + } | |
2109 | + } | |
2110 | + i = digitsToWords(prec1) | |
2111 | + len1 := i + digitsToWords(2*frac2+fracIncr+1) + 1 | |
2112 | + if len1 < 3 { | |
2113 | + len1 = 3 | |
2114 | + } | |
2115 | + | |
2116 | + tmp1 := make([]int32, len1) | |
2117 | + copy(tmp1, from1.wordBuf[idx1:idx1+i]) | |
2118 | + | |
2119 | + start1 := 0 | |
2120 | + var stop1 int | |
2121 | + start2 := idx2 | |
2122 | + stop2 := idx2 + digitsToWords(prec2) - 1 | |
2123 | + | |
2124 | + /* removing end zeroes */ | |
2125 | + for from2.wordBuf[stop2] == 0 && stop2 >= start2 { | |
2126 | + stop2-- | |
2127 | + } | |
2128 | + len2 := stop2 - start2 | |
2129 | + stop2++ | |
2130 | + | |
2131 | + /* | |
2132 | + calculating norm2 (normalized from2.wordBuf[start2]) - we need from2.wordBuf[start2] to be large | |
2133 | + (at least > DIG_BASE/2), but unlike Knuth's Alg. D we don't want to | |
2134 | + normalize input numbers (as we don't make a copy of the divisor). | |
2135 | + Thus we normalize first dec1 of buf2 only, and we'll normalize tmp1[start1] | |
2136 | + on the fly for the purpose of guesstimation only. | |
2137 | + It's also faster, as we're saving on normalization of from2. | |
2138 | + */ | |
2139 | + normFactor := wordBase / int64(from2.wordBuf[start2]+1) | |
2140 | + norm2 := int32(normFactor * int64(from2.wordBuf[start2])) | |
2141 | + if len2 > 0 { | |
2142 | + norm2 += int32(normFactor * int64(from2.wordBuf[start2+1]) / wordBase) | |
2143 | + } | |
2144 | + dcarry := int32(0) | |
2145 | + if tmp1[start1] < from2.wordBuf[start2] { | |
2146 | + dcarry = tmp1[start1] | |
2147 | + start1++ | |
2148 | + } | |
2149 | + | |
2150 | + // main loop | |
2151 | + var guess int64 | |
2152 | + for ; idxTo < stopTo; idxTo++ { | |
2153 | + /* short-circuit, if possible */ | |
2154 | + if dcarry == 0 && tmp1[start1] < from2.wordBuf[start2] { | |
2155 | + guess = 0 | |
2156 | + } else { | |
2157 | + /* D3: make a guess */ | |
2158 | + x := int64(tmp1[start1]) + int64(dcarry)*wordBase | |
2159 | + y := int64(tmp1[start1+1]) | |
2160 | + guess = (normFactor*x + normFactor*y/wordBase) / int64(norm2) | |
2161 | + if guess >= wordBase { | |
2162 | + guess = wordBase - 1 | |
2163 | + } | |
2164 | + | |
2165 | + if len2 > 0 { | |
2166 | + /* remove normalization */ | |
2167 | + if int64(from2.wordBuf[start2+1])*guess > (x-guess*int64(from2.wordBuf[start2]))*wordBase+y { | |
2168 | + guess-- | |
2169 | + } | |
2170 | + if int64(from2.wordBuf[start2+1])*guess > (x-guess*int64(from2.wordBuf[start2]))*wordBase+y { | |
2171 | + guess-- | |
2172 | + } | |
2173 | + } | |
2174 | + | |
2175 | + /* D4: multiply and subtract */ | |
2176 | + idx2 = stop2 | |
2177 | + idx1 = start1 + len2 | |
2178 | + var carry int32 | |
2179 | + for carry = 0; idx2 > start2; idx1-- { | |
2180 | + var hi, lo int32 | |
2181 | + idx2-- | |
2182 | + x = guess * int64(from2.wordBuf[idx2]) | |
2183 | + hi = int32(x / wordBase) | |
2184 | + lo = int32(x - int64(hi)*wordBase) | |
2185 | + tmp1[idx1], carry = sub2(tmp1[idx1], lo, carry) | |
2186 | + carry += hi | |
2187 | + } | |
2188 | + if dcarry < carry { | |
2189 | + carry = 1 | |
2190 | + } else { | |
2191 | + carry = 0 | |
2192 | + } | |
2193 | + | |
2194 | + /* D5: check the remainder */ | |
2195 | + if carry > 0 { | |
2196 | + /* D6: correct the guess */ | |
2197 | + guess-- | |
2198 | + idx2 = stop2 | |
2199 | + idx1 = start1 + len2 | |
2200 | + for carry = 0; idx2 > start2; idx1-- { | |
2201 | + idx2-- | |
2202 | + tmp1[idx1], carry = add(tmp1[idx1], from2.wordBuf[idx2], carry) | |
2203 | + } | |
2204 | + } | |
2205 | + } | |
2206 | + if mod == nil { | |
2207 | + to.wordBuf[idxTo] = int32(guess) | |
2208 | + } | |
2209 | + dcarry = tmp1[start1] | |
2210 | + start1++ | |
2211 | + } | |
2212 | + if mod != nil { | |
2213 | + /* | |
2214 | + now the result is in tmp1, it has | |
2215 | + digitsInt=prec1-frac1 | |
2216 | + digitsFrac=max(frac1, frac2) | |
2217 | + */ | |
2218 | + if dcarry != 0 { | |
2219 | + start1-- | |
2220 | + tmp1[start1] = dcarry | |
2221 | + } | |
2222 | + idxTo = 0 | |
2223 | + | |
2224 | + digitsIntTo = prec1 - frac1 - start1*digitsPerWord | |
2225 | + if digitsIntTo < 0 { | |
2226 | + /* If leading zeroes in the fractional part were earlier stripped */ | |
2227 | + wordsIntTo = digitsIntTo / digitsPerWord | |
2228 | + } else { | |
2229 | + wordsIntTo = digitsToWords(digitsIntTo) | |
2230 | + } | |
2231 | + | |
2232 | + wordsFracTo = digitsToWords(int(to.digitsFrac)) | |
2233 | + err = nil | |
2234 | + if wordsIntTo == 0 && wordsFracTo == 0 { | |
2235 | + *to = zeroMyDecimal | |
2236 | + return err | |
2237 | + } | |
2238 | + if wordsIntTo <= 0 { | |
2239 | + if -wordsIntTo >= wordBufLen { | |
2240 | + *to = zeroMyDecimal | |
2241 | + return ErrTruncated | |
2242 | + } | |
2243 | + stop1 = start1 + wordsIntTo + wordsFracTo | |
2244 | + wordsFracTo += wordsIntTo | |
2245 | + to.digitsInt = 0 | |
2246 | + for wordsIntTo < 0 { | |
2247 | + to.wordBuf[idxTo] = 0 | |
2248 | + idxTo++ | |
2249 | + wordsIntTo++ | |
2250 | + } | |
2251 | + } else { | |
2252 | + if wordsIntTo > wordBufLen { | |
2253 | + to.digitsInt = int8(digitsPerWord * wordBufLen) | |
2254 | + to.digitsFrac = 0 | |
2255 | + return ErrOverflow | |
2256 | + } | |
2257 | + stop1 = start1 + wordsIntTo + wordsFracTo | |
2258 | + to.digitsInt = int8(myMin(wordsIntTo*digitsPerWord, int(from2.digitsInt))) | |
2259 | + } | |
2260 | + if wordsIntTo+wordsFracTo > wordBufLen { | |
2261 | + stop1 -= wordsIntTo + wordsFracTo - wordBufLen | |
2262 | + wordsFracTo = wordBufLen - wordsIntTo | |
2263 | + to.digitsFrac = int8(wordsFracTo * digitsPerWord) | |
2264 | + err = ErrTruncated | |
2265 | + } | |
2266 | + for start1 < stop1 { | |
2267 | + to.wordBuf[idxTo] = tmp1[start1] | |
2268 | + idxTo++ | |
2269 | + start1++ | |
2270 | + } | |
2271 | + } | |
2272 | + idxTo, digitsIntTo = to.removeLeadingZeros() | |
2273 | + to.digitsInt = int8(digitsIntTo) | |
2274 | + if idxTo != 0 { | |
2275 | + copy(to.wordBuf[:], to.wordBuf[idxTo:]) | |
2276 | + } | |
2277 | + return err | |
2278 | +} | |
2279 | + | |
2280 | +// DecimalPeak returns the length of the encoded decimal. | |
2281 | +func DecimalPeak(b []byte) (int, error) { | |
2282 | + if len(b) < 3 { | |
2283 | + return 0, ErrBadNumber | |
2284 | + } | |
2285 | + precision := int(b[0]) | |
2286 | + frac := int(b[1]) | |
2287 | + return DecimalBinSize(precision, frac) + 2, nil | |
2288 | +} | |
2289 | + | |
2290 | +// NewDecFromInt creates a MyDecimal from int. | |
2291 | +func NewDecFromInt(i int64) *MyDecimal { | |
2292 | + return new(MyDecimal).FromInt(i) | |
2293 | +} | |
2294 | + | |
2295 | +// NewDecFromUint creates a MyDecimal from uint. | |
2296 | +func NewDecFromUint(i uint64) *MyDecimal { | |
2297 | + return new(MyDecimal).FromUint(i) | |
2298 | +} | |
2299 | + | |
2300 | +// NewDecFromFloatForTest creates a MyDecimal from float, as it returns no error, it should only be used in test. | |
2301 | +func NewDecFromFloatForTest(f float64) *MyDecimal { | |
2302 | + dec := new(MyDecimal) | |
2303 | + err := dec.FromFloat64(f) | |
2304 | + Log(err) | |
2305 | + return dec | |
2306 | +} | |
2307 | + | |
2308 | +// NewDecFromStringForTest creates a MyDecimal from string, as it returns no error, it should only be used in test. | |
2309 | +func NewDecFromStringForTest(s string) *MyDecimal { | |
2310 | + dec := new(MyDecimal) | |
2311 | + err := dec.FromString([]byte(s)) | |
2312 | + Log(err) | |
2313 | + return dec | |
2314 | +} | |
2315 | + | |
2316 | +// NewMaxOrMinDec returns the max or min value decimal for given precision and fraction. | |
2317 | +func NewMaxOrMinDec(negative bool, prec, frac int) *MyDecimal { | |
2318 | + str := make([]byte, prec+2) | |
2319 | + for i := 0; i < len(str); i++ { | |
2320 | + str[i] = '9' | |
2321 | + } | |
2322 | + if negative { | |
2323 | + str[0] = '-' | |
2324 | + } else { | |
2325 | + str[0] = '+' | |
2326 | + } | |
2327 | + str[1+prec-frac] = '.' | |
2328 | + dec := new(MyDecimal) | |
2329 | + err := dec.FromString(str) | |
2330 | + Log(err) | |
2331 | + return dec | |
2332 | +} |