1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.tikv.common.codec;
19
20 import com.google.common.annotations.VisibleForTesting;
21 import java.io.Serializable;
22 import java.math.BigDecimal;
23 import java.math.BigInteger;
24 import java.util.Arrays;
25
26
27
28 public class MyDecimal implements Serializable {
29
30 private static final int digitsPerWord = 9;
31
32 private static final int wordBufLen = 9;
33
34 private static final int wordSize = 4;
35 private static final int ten0 = 1;
36 private static final int ten1 = 10;
37 private static final int ten2 = 100;
38 private static final int ten3 = 1000;
39 private static final int ten4 = 10000;
40 private static final int ten5 = 100000;
41 private static final int ten6 = 1000000;
42 private static final int ten7 = 10000000;
43 private static final int ten8 = 100000000;
44 private static final int ten9 = 1000000000;
45 private static final int digMask = ten8;
46 private static final int wordBase = ten9;
47 private static final BigInteger wordBaseBigInt = BigInteger.valueOf(ten9);
48 private static final int wordMax = wordBase - 1;
49 private static final int[] div9 =
50 new int[] {
51 0, 0, 0, 0, 0, 0, 0, 0, 0,
52 1, 1, 1, 1, 1, 1, 1, 1, 1,
53 2, 2, 2, 2, 2, 2, 2, 2, 2,
54 3, 3, 3, 3, 3, 3, 3, 3, 3,
55 4, 4, 4, 4, 4, 4, 4, 4, 4,
56 5, 5, 5, 5, 5, 5, 5, 5, 5,
57 6, 6, 6, 6, 6, 6, 6, 6, 6,
58 7, 7, 7, 7, 7, 7, 7, 7, 7,
59 8, 8, 8, 8, 8, 8, 8, 8, 8,
60 9, 9, 9, 9, 9, 9, 9, 9, 9,
61 10, 10, 10, 10, 10, 10, 10, 10, 10,
62 11, 11, 11, 11, 11, 11, 11, 11, 11,
63 12, 12, 12, 12, 12, 12, 12, 12, 12,
64 13, 13, 13, 13, 13, 13, 13, 13, 13,
65 14, 14,
66 };
67 private static final int[] powers10 =
68 new int[] {ten0, ten1, ten2, ten3, ten4, ten5, ten6, ten7, ten8, ten9};
69
70 private static final BigInteger[] powers10BigInt =
71 new BigInteger[] {
72 BigInteger.valueOf(ten0),
73 BigInteger.valueOf(ten1),
74 BigInteger.valueOf(ten2),
75 BigInteger.valueOf(ten3),
76 BigInteger.valueOf(ten4),
77 BigInteger.valueOf(ten5),
78 BigInteger.valueOf(ten6),
79 BigInteger.valueOf(ten7),
80 BigInteger.valueOf(ten8),
81 BigInteger.valueOf(ten9)
82 };
83
84
85 private static final int maxWordBufLen = 9;
86 private static final int maxFraction = 30;
87 private static final int[] dig2bytes = new int[] {0, 1, 1, 2, 2, 3, 3, 4, 4, 4};
88
89
90 private int digitsInt;
91 private int digitsFrac;
92 private boolean negative;
93 private int[] wordBuf = new int[maxWordBufLen];
94
95 public MyDecimal() {}
96
97 public MyDecimal(int digitsInt, int digitsFrac, boolean negative, int[] wordBuf) {
98 this.digitsInt = digitsInt;
99 this.digitsFrac = digitsFrac;
100 this.negative = negative;
101 this.wordBuf = wordBuf;
102 }
103
104
105
106
107
108
109
110
111 @VisibleForTesting
112 public static int readWord(int[] b, int size, int start) {
113 int x = 0;
114 switch (size) {
115 case 1:
116 x = (byte) b[start];
117 break;
118 case 2:
119 x = (((byte) b[start]) << 8) + (b[start + 1] & 0xFF);
120 break;
121 case 3:
122 int sign = b[start] & 128;
123 if (sign > 0) {
124 x = 0xFF << 24 | (b[start] << 16) | (b[start + 1] << 8) | (b[start + 2]);
125 } else {
126 x = b[start] << 16 | (b[start + 1] << 8) | b[start + 2];
127 }
128 break;
129 case 4:
130 x = b[start + 3] + (b[start + 2] << 8) + (b[start + 1] << 16) + (b[start] << 24);
131 break;
132 }
133 return x;
134 }
135
136
137
138
139
140
141 public int precision() {
142 int frac = this.digitsFrac;
143 int digitsInt =
144 this.removeLeadingZeros()[
145 1];
146 int precision = digitsInt + frac;
147
148 if (precision == 0) {
149 precision = 1;
150 }
151 return precision;
152 }
153
154
155
156
157
158 public int frac() {
159 return digitsFrac;
160 }
161
162
163
164
165
166
167 public void fromDecimal(double value) {
168 String s = Double.toString(value);
169 this.fromString(s);
170 }
171
172
173
174
175
176
177
178
179 public int fromBin(int precision, int frac, int[] bin) {
180 if (bin.length == 0) {
181 throw new IllegalArgumentException("Bad Float Number to parse");
182 }
183
184 int _digitsInt = precision - frac;
185 int _wordsInt = _digitsInt / digitsPerWord;
186 int _leadingDigits = _digitsInt - _wordsInt * digitsPerWord;
187 int _wordsFrac = frac / digitsPerWord;
188 int trailingDigits = frac - _wordsFrac * digitsPerWord;
189 int wordsIntTo = _wordsInt;
190 if (_leadingDigits > 0) {
191 wordsIntTo++;
192 }
193 int wordsFracTo = _wordsFrac;
194 if (trailingDigits > 0) {
195 wordsFracTo++;
196 }
197
198 int binIdx = 0;
199 int mask = -1;
200 int sign = bin[binIdx] & 0x80;
201 if (sign > 0) {
202 mask = 0;
203 }
204 int binSize = decimalBinSize(precision, frac);
205 int[] dCopy;
206 dCopy = Arrays.copyOf(bin, binSize);
207 dCopy[0] ^= 0x80;
208 bin = dCopy;
209
210 int oldWordsIntTo = wordsIntTo;
211 boolean overflow = false;
212 boolean truncated = false;
213 if (wordsIntTo + wordsFracTo > wordBufLen) {
214 if (wordsIntTo > wordBufLen) {
215 wordsIntTo = wordBufLen;
216 wordsFracTo = 0;
217 overflow = true;
218 } else {
219 wordsIntTo = _wordsInt;
220 wordsFracTo = wordBufLen - _wordsInt;
221 truncated = true;
222 }
223 }
224
225 if (overflow || truncated) {
226 if (wordsIntTo < oldWordsIntTo) {
227 binIdx += dig2bytes[_leadingDigits] + (_wordsInt - wordsIntTo) * wordSize;
228 } else {
229 trailingDigits = 0;
230 _wordsFrac = wordsFracTo;
231 }
232 }
233
234 this.negative = mask != 0;
235 this.digitsInt = (byte) (_wordsInt * digitsPerWord + _leadingDigits);
236 this.digitsFrac = (byte) (_wordsFrac * digitsPerWord + trailingDigits);
237
238 int wordIdx = 0;
239 if (_leadingDigits > 0) {
240 int i = dig2bytes[_leadingDigits];
241 int x = readWord(bin, i, binIdx);
242 binIdx += i;
243 this.wordBuf[wordIdx] = (x ^ mask) > 0 ? x ^ mask : (x ^ mask) & 0xFF;
244 if (this.wordBuf[wordIdx] >= powers10[_leadingDigits + 1]) {
245 throw new IllegalArgumentException("BadNumber");
246 }
247 if (this.wordBuf[wordIdx] != 0) {
248 wordIdx++;
249 } else {
250 this.digitsInt -= _leadingDigits;
251 }
252 }
253 for (int stop = binIdx + _wordsInt * wordSize; binIdx < stop; binIdx += wordSize) {
254 this.wordBuf[wordIdx] = (readWord(bin, 4, binIdx) ^ mask);
255 if (this.wordBuf[wordIdx] > wordMax) {
256 throw new IllegalArgumentException("BadNumber");
257 }
258 if (wordIdx > 0 || this.wordBuf[wordIdx] != 0) {
259 wordIdx++;
260 } else {
261 this.digitsInt -= digitsPerWord;
262 }
263 }
264
265 for (int stop = binIdx + _wordsFrac * wordSize; binIdx < stop; binIdx += wordSize) {
266 int x = readWord(bin, 4, binIdx);
267 this.wordBuf[wordIdx] = (x ^ mask) > 0 ? x ^ mask : (x ^ mask) & 0xFF;
268 if (this.wordBuf[wordIdx] > wordMax) {
269 throw new IllegalArgumentException("BadNumber");
270 }
271 wordIdx++;
272 }
273
274 if (trailingDigits > 0) {
275 int i = dig2bytes[trailingDigits];
276 int x = readWord(bin, i, binIdx);
277 this.wordBuf[wordIdx] =
278 ((x ^ mask) > 0 ? x ^ mask : (x ^ mask) & 0xFF)
279 * powers10[digitsPerWord - trailingDigits];
280 if (this.wordBuf[wordIdx] > wordMax) {
281 throw new IllegalArgumentException("BadNumber");
282 }
283 }
284
285 return binSize;
286 }
287
288
289 private int[] removeLeadingZeros() {
290 int wordIdx = 0;
291 int digitsInt = this.digitsInt;
292 int i = ((digitsInt - 1) % digitsPerWord) + 1;
293 for (; digitsInt > 0 && this.wordBuf[wordIdx] == 0; ) {
294 digitsInt -= i;
295 i = digitsPerWord;
296 wordIdx++;
297 }
298 if (digitsInt > 0) {
299 digitsInt -= countLeadingZeroes((digitsInt - 1) % digitsPerWord, this.wordBuf[wordIdx]);
300 } else {
301 digitsInt = 0;
302 }
303 int[] res = new int[2];
304 res[0] = wordIdx;
305 res[1] = digitsInt;
306 return res;
307 }
308
309
310
311
312
313
314
315 private int countLeadingZeroes(int i, int word) {
316 int leading = 0;
317 for (; word < powers10[i]; ) {
318 i--;
319 leading++;
320 }
321 return leading;
322 }
323
324
325 private int digitsToWords(int digits) {
326 if ((digits + digitsPerWord - 1) >= 0 && ((digits + digitsPerWord - 1) < 128)) {
327 return div9[digits + digitsPerWord - 1];
328 }
329 return (digits + digitsPerWord - 1) / digitsPerWord;
330 }
331
332
333
334
335
336
337 @VisibleForTesting
338 public void fromString(String s) {
339 char[] sCharArray = s.toCharArray();
340 fromCharArray(sCharArray);
341 }
342
343
344 private void fromCharArray(char[] str) {
345 int startIdx = 0;
346
347 for (; startIdx < str.length; startIdx++) {
348 if (!Character.isSpaceChar(str[startIdx])) {
349 break;
350 }
351 }
352
353 if (str.length == 0) {
354 throw new IllegalArgumentException("BadNumber");
355 }
356
357
358
359
360
361 switch (str[startIdx]) {
362 case '-':
363 this.negative = true;
364 startIdx++;
365 break;
366 case '+':
367 startIdx++;
368 break;
369 }
370 int strIdx = startIdx;
371 for (; strIdx < str.length && Character.isDigit(str[strIdx]); ) {
372 strIdx++;
373 }
374
375
376 int digitsInt = strIdx - startIdx;
377 int digitsFrac;
378 int endIdx;
379 if (strIdx < str.length && str[strIdx] == '.') {
380 endIdx = strIdx + 1;
381
382 for (; endIdx < str.length && Character.isDigit(str[endIdx]); ) {
383 endIdx++;
384 }
385 digitsFrac = endIdx - strIdx - 1;
386 } else {
387 digitsFrac = 0;
388 }
389
390 if (digitsInt + digitsFrac == 0) {
391 throw new IllegalArgumentException("BadNumber");
392 }
393 int wordsInt = digitsToWords(digitsInt);
394 int wordsFrac = digitsToWords(digitsFrac);
395
396
397 boolean overflow = false;
398 boolean truncated = false;
399 if (wordsInt + wordsFrac > wordBufLen) {
400 if (wordsInt > wordBufLen) {
401 wordsInt = wordBufLen;
402 wordsFrac = 0;
403 overflow = true;
404 } else {
405 wordsFrac = wordBufLen - wordsInt;
406 truncated = true;
407 }
408 }
409
410 if (overflow || truncated) {
411 digitsFrac = wordsFrac * digitsPerWord;
412 if (overflow) {
413 digitsInt = wordsInt * digitsPerWord;
414 }
415 }
416 this.digitsInt = digitsInt;
417 this.digitsFrac = digitsFrac;
418 int wordIdx = wordsInt;
419 int strIdxTmp = strIdx;
420 int word = 0;
421 int innerIdx = 0;
422 for (; digitsInt > 0; ) {
423 digitsInt--;
424 strIdx--;
425 word += (str[strIdx] - '0') * powers10[innerIdx];
426 innerIdx++;
427 if (innerIdx == digitsPerWord) {
428 wordIdx--;
429 this.wordBuf[wordIdx] = word;
430 word = 0;
431 innerIdx = 0;
432 }
433 }
434
435 if (innerIdx != 0) {
436 wordIdx--;
437 this.wordBuf[wordIdx] = word;
438 }
439
440 wordIdx = wordsInt;
441 strIdx = strIdxTmp;
442 word = 0;
443 innerIdx = 0;
444
445 for (; digitsFrac > 0; ) {
446 digitsFrac--;
447 strIdx++;
448 word = (str[strIdx] - '0') + word * 10;
449 innerIdx++;
450 if (innerIdx == digitsPerWord) {
451 this.wordBuf[wordIdx] = word;
452 wordIdx++;
453 word = 0;
454 innerIdx = 0;
455 }
456 }
457 if (innerIdx != 0) {
458 this.wordBuf[wordIdx] = word * powers10[digitsPerWord - innerIdx];
459 }
460
461
462 boolean allZero = true;
463 for (int i = 0; i < wordBufLen; i++) {
464 if (this.wordBuf[i] != 0) {
465 allZero = false;
466 break;
467 }
468 }
469 if (allZero) {
470 this.negative = false;
471 }
472 }
473
474
475 @Override
476 public String toString() {
477 char[] str;
478
479 int _digitsFrac = this.digitsFrac;
480 int[] res = removeLeadingZeros();
481 int wordStartIdx = res[0];
482 int digitsInt = res[1];
483 if (digitsInt + _digitsFrac == 0) {
484 digitsInt = 1;
485 wordStartIdx = 0;
486 }
487
488 int digitsIntLen = digitsInt;
489 if (digitsIntLen == 0) {
490 digitsIntLen = 1;
491 }
492 int digitsFracLen = _digitsFrac;
493 int length = digitsIntLen + digitsFracLen;
494 if (this.negative) {
495 length++;
496 }
497 if (_digitsFrac > 0) {
498 length++;
499 }
500 str = new char[length];
501
502 int strIdx = 0;
503 if (this.negative) {
504 str[strIdx] = '-';
505 strIdx++;
506 }
507
508 if (_digitsFrac > 0) {
509 int fracIdx = strIdx + digitsIntLen;
510 int wordIdx = wordStartIdx + digitsToWords(digitsInt);
511 str[fracIdx] = '.';
512 fracIdx++;
513 for (; _digitsFrac > 0; _digitsFrac -= digitsPerWord) {
514 int x = this.wordBuf[wordIdx];
515 wordIdx++;
516 for (int i = Math.min(_digitsFrac, MyDecimal.digitsPerWord); i > 0; i--) {
517 int y = x / digMask;
518 str[fracIdx] = (char) ((char) y + '0');
519 fracIdx++;
520 x -= y * digMask;
521 x *= 10;
522 }
523 }
524 }
525 if (digitsInt > 0) {
526 strIdx += digitsInt;
527 int wordIdx = wordStartIdx + digitsToWords(digitsInt);
528 for (; digitsInt > 0; digitsInt -= digitsPerWord) {
529 wordIdx--;
530 int x = this.wordBuf[wordIdx];
531 for (int i = Math.min(digitsInt, MyDecimal.digitsPerWord); i > 0; i--) {
532 int temp = x / 10;
533 strIdx--;
534 str[strIdx] = (char) ('0' + (x - temp * 10));
535 x = temp;
536 }
537 }
538 } else {
539 str[strIdx] = '0';
540 }
541
542 return new String(str);
543 }
544
545
546 private int decimalBinSize(int precision, int frac) {
547 int digitsInt = precision - frac;
548 int wordsInt = digitsInt / digitsPerWord;
549 int wordsFrac = frac / digitsPerWord;
550 int xInt = digitsInt - wordsInt * digitsPerWord;
551 int xFrac = frac - wordsFrac * digitsPerWord;
552 return wordsInt * wordSize + dig2bytes[xInt] + wordsFrac * wordSize + dig2bytes[xFrac];
553 }
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620 public int[] toBin(int precision, int frac) {
621 if (precision > digitsPerWord * maxWordBufLen
622 || precision < 0
623 || frac > maxFraction
624 || frac < 0) {
625 throw new IllegalArgumentException("BadNumber");
626 }
627
628 int mask = 0;
629 if (this.negative) {
630 mask = -1;
631 }
632
633 int digitsInt = precision - frac;
634 int wordsInt = digitsInt / digitsPerWord;
635 int leadingDigits = digitsInt - wordsInt * digitsPerWord;
636 int wordsFrac = frac / digitsPerWord;
637 int trailingDigits = frac - wordsFrac * digitsPerWord;
638
639
640 int wordsFracFrom = this.digitsFrac / digitsPerWord;
641 int trailingDigitsFrom = this.digitsFrac - wordsFracFrom * digitsPerWord;
642 int intSize = wordsInt * wordSize + dig2bytes[leadingDigits];
643 int fracSize = wordsFrac * wordSize + dig2bytes[trailingDigits];
644 int fracSizeFrom = wordsFracFrom * wordSize + dig2bytes[trailingDigitsFrom];
645 int originIntSize = intSize;
646 int originFracSize = fracSize;
647 int[] bin = new int[intSize + fracSize];
648 int binIdx = 0;
649 int[] res = this.removeLeadingZeros();
650 int wordIdxFrom = res[0];
651 int digitsIntFrom = res[1];
652 if (digitsIntFrom + fracSizeFrom == 0) {
653 mask = 0;
654 digitsInt = 1;
655 }
656
657 int wordsIntFrom = digitsIntFrom / digitsPerWord;
658 int leadingDigitsFrom = digitsIntFrom - wordsIntFrom * digitsPerWord;
659 int iSizeFrom = wordsIntFrom * wordSize + dig2bytes[leadingDigitsFrom];
660
661 if (digitsInt < digitsIntFrom) {
662 wordIdxFrom += (wordsIntFrom - wordsInt);
663 if (leadingDigitsFrom > 0) {
664 wordIdxFrom++;
665 }
666
667 if (leadingDigits > 0) {
668 wordIdxFrom--;
669 }
670
671 wordsIntFrom = wordsInt;
672 leadingDigitsFrom = leadingDigits;
673
674 } else if (intSize > iSizeFrom) {
675 for (; intSize > iSizeFrom; ) {
676 intSize--;
677 bin[binIdx] = mask & 0xff;
678 binIdx++;
679 }
680 }
681
682
683 if (fracSize < fracSizeFrom) {
684 wordsFracFrom = wordsFrac;
685 trailingDigitsFrom = trailingDigits;
686
687 } else if (fracSize > fracSizeFrom && trailingDigitsFrom > 0) {
688 if (wordsFrac == wordsFracFrom) {
689 trailingDigitsFrom = trailingDigits;
690 fracSize = fracSizeFrom;
691 } else {
692 wordsFracFrom++;
693 trailingDigitsFrom = 0;
694 }
695 }
696
697
698 if (leadingDigitsFrom > 0) {
699 int i = dig2bytes[leadingDigitsFrom];
700 int x = (this.wordBuf[wordIdxFrom] % powers10[leadingDigitsFrom]) ^ mask;
701 wordIdxFrom++;
702 writeWord(bin, x, i, binIdx);
703 binIdx += i;
704 }
705
706
707 for (int stop = wordIdxFrom + wordsIntFrom + wordsFracFrom;
708 wordIdxFrom < stop;
709 binIdx += wordSize) {
710 int x = this.wordBuf[wordIdxFrom] ^ mask;
711 wordIdxFrom++;
712 writeWord(bin, x, 4, binIdx);
713 }
714
715 if (trailingDigitsFrom > 0) {
716 int x;
717 int i = dig2bytes[trailingDigitsFrom];
718 int lim = trailingDigits;
719 if (wordsFracFrom < wordsFrac) {
720 lim = digitsPerWord;
721 }
722
723 for (; trailingDigitsFrom < lim && dig2bytes[trailingDigitsFrom] == i; ) {
724 trailingDigitsFrom++;
725 }
726 x = (this.wordBuf[wordIdxFrom] / powers10[digitsPerWord - trailingDigitsFrom]) ^ mask;
727 writeWord(bin, x, i, binIdx);
728 binIdx += i;
729 }
730
731 if (fracSize > fracSizeFrom) {
732 int binIdxEnd = originIntSize + originFracSize;
733 for (; fracSize > fracSizeFrom && binIdx < binIdxEnd; ) {
734 fracSize--;
735 bin[binIdx] = mask & 0xff;
736 binIdx++;
737 }
738 }
739 bin[0] ^= 0x80;
740 return bin;
741 }
742
743
744 private void writeWord(int[] b, int word, int size, int start) {
745 switch (size) {
746 case 1:
747 b[start] = word & 0xFF;
748 break;
749 case 2:
750 b[start] = (word >>> 8) & 0xFF;
751 b[start + 1] = word & 0xFF;
752 break;
753 case 3:
754 b[start] = (word >>> 16) & 0xFF;
755 b[start + 1] = (word >>> 8) & 0xFF;
756 b[start + 2] = word & 0xFF;
757 break;
758 case 4:
759 b[start] = (word >>> 24) & 0xFF;
760 b[start + 1] = (word >>> 16) & 0xFF;
761 b[start + 2] = (word >>> 8) & 0xFF;
762 b[start + 3] = word & 0xFF;
763 break;
764 }
765 }
766
767
768 public void clear() {
769 this.digitsFrac = 0;
770 this.digitsInt = 0;
771 this.negative = false;
772 }
773
774 private BigInteger toBigInteger() {
775 BigInteger x = BigInteger.ZERO;
776 int wordIdx = 0;
777 for (int i = this.digitsInt; i > 0; i -= digitsPerWord) {
778 x = x.multiply(wordBaseBigInt).add(BigInteger.valueOf(this.wordBuf[wordIdx]));
779 wordIdx++;
780 }
781
782 for (int i = this.digitsFrac; i > 0; i -= digitsPerWord) {
783 x = x.multiply(wordBaseBigInt).add(BigInteger.valueOf(this.wordBuf[wordIdx]));
784 wordIdx++;
785 }
786
787 if (digitsFrac % digitsPerWord != 0) {
788 x = x.divide(powers10BigInt[digitsPerWord - digitsFrac % digitsPerWord]);
789 }
790 if (negative) {
791 x = x.negate();
792 }
793 return x;
794 }
795
796 public long toLong() {
797 long x = 0;
798 int wordIdx = 0;
799 for (int i = this.digitsInt; i > 0; i -= digitsPerWord) {
800 x = x * wordBase + this.wordBuf[wordIdx];
801 wordIdx++;
802 }
803
804 for (int i = this.digitsFrac; i > 0; i -= digitsPerWord) {
805 x = x * wordBase + this.wordBuf[wordIdx];
806 wordIdx++;
807 }
808
809 if (digitsFrac % digitsPerWord != 0) {
810 x = x / powers10[digitsPerWord - digitsFrac % digitsPerWord];
811 }
812
813 if (negative) {
814 x = -x;
815 }
816 return x;
817 }
818
819 public BigDecimal toBigDecimal() {
820
821
822
823 if (digitsInt + digitsFrac < 19) {
824 return new BigDecimal(BigInteger.valueOf(toLong()), digitsFrac);
825 }
826 return new BigDecimal(toBigInteger(), digitsFrac);
827 }
828 }