View Javadoc
1   /*
2    * Copyright 2021 TiKV Project Authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
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  // TODO: We shouldn't allow empty MyDecimal
27  // TODO: It seems MyDecimal to BigDecimal is very slow
28  public class MyDecimal implements Serializable {
29    // how many digits that a word has
30    private static final int digitsPerWord = 9;
31    // MyDecimal can holds at most 9 words.
32    private static final int wordBufLen = 9;
33    // A word is 4 bytes int
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    // A MyDecimal holds 9 words.
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    // The following are fields of MyDecimal
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    * Reads a word from a array at given size.
106    *
107    * @param b b is source data of unsigned byte as int[]
108    * @param size is word size which can be used in switch statement.
109    * @param start start indicates the where start to read.
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    * Returns total precision of this decimal. Basically, it is sum of digitsInt and digitsFrac. But there
138    * are some special cases need to be token care of such as 000.001.
139    * Precision reflects the actual effective precision without leading zero
140    */
141   public int precision() {
142     int frac = this.digitsFrac;
143     int digitsInt =
144         this.removeLeadingZeros()[
145             1]; /*this function return an array and the second element is digitsInt*/
146     int precision = digitsInt + frac;
147     // if no precision, it is just 0.
148     if (precision == 0) {
149       precision = 1;
150     }
151     return precision;
152   }
153 
154   /**
155    * Returns fraction digits that counts how many digits after ".". frac() reflects the actual
156    * effective fraction without trailing zero
157    */
158   public int frac() {
159     return digitsFrac;
160   }
161 
162   /**
163    * Parses a decimal value from a string
164    *
165    * @param value an double value
166    */
167   public void fromDecimal(double value) {
168     String s = Double.toString(value);
169     this.fromString(s);
170   }
171 
172   /**
173    * Parses a decimal from binary string for given precision and frac.
174    *
175    * @param precision precision specifies total digits that this decimal will be..
176    * @param frac frac specifies how many fraction digits
177    * @param bin bin is binary string which represents a decimal value.
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   /** Truncates any prefix zeros such as 00.001. After this, digitsInt is truncated from 2 to 0. */
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    * Counts the number of digits of prefix zeroes. For 00.001, it returns two.
311    *
312    * @param i i is index for getting powers10.
313    * @param word word is a integer.
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   /** Returns size of word for a give value with number of digits */
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    * parser a decimal value from a string.
334    *
335    * @param s s is a decimal in string form.
336    */
337   @VisibleForTesting
338   public void fromString(String s) {
339     char[] sCharArray = s.toCharArray();
340     fromCharArray(sCharArray);
341   }
342 
343   // helper function for fromString
344   private void fromCharArray(char[] str) {
345     int startIdx = 0;
346     // found first character is not space and start from here
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     // skip sign and record where digits start from
358     // [-, 1, 2, 3]
359     // [+, 1, 2, 3]
360     // for +/-, we need skip them and record sign information into negative field.
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     // we initialize strIdx in case of sign notation, here we need subtract startIdx from strIdx
375     // cause strIdx is used for counting the number of digits.
376     int digitsInt = strIdx - startIdx;
377     int digitsFrac;
378     int endIdx;
379     if (strIdx < str.length && str[strIdx] == '.') {
380       endIdx = strIdx + 1;
381       // detect where is the end index of this char array.
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     // TODO the following code are fixWordCntError such as overflow and truncated error
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     // this is -0000 is just 0.
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   // Returns a decimal string.
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   // decimalBinSize returns the size of array to hold a binary representation of a decimal.
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    * ToBin converts decimal to its binary fixed-length representation two representations of the
557    * same length can be compared with memcmp with the correct -1/0/+1 result
558    *
559    * <p>PARAMS precision/frac - if precision is 0, internal value of the decimal will be used, then
560    * the encoded value is not memory comparable.
561    *
562    * <p>NOTE the buffer is assumed to be of the size decimalBinSize(precision, frac)
563    *
564    * <p>RETURN VALUE bin - binary value errCode - eDecOK/eDecTruncate/eDecOverflow
565    *
566    * <p>DESCRIPTION for storage decimal numbers are converted to the "binary" format.
567    *
568    * <p>This format has the following properties: 1. length of the binary representation depends on
569    * the {precision, frac} as provided by the caller and NOT on the digitsInt/digitsFrac of the
570    * decimal to convert. 2. binary representations of the same {precision, frac} can be compared
571    * with memcmp - with the same result as DecimalCompare() of the original decimals (not taking
572    * into account possible precision loss during conversion).
573    *
574    * <p>This binary format is as follows: 1. First the number is converted to have a requested
575    * precision and frac. 2. Every full digitsPerWord digits of digitsInt part are stored in 4 bytes
576    * as is 3. The first digitsInt % digitsPerWord digits are stored in the reduced number of bytes
577    * (enough bytes to store this number of digits - see dig2bytes) 4. same for frac - full word are
578    * stored as is, the last frac % digitsPerWord digits - in the reduced number of bytes. 5. If the
579    * number is negative - every byte is inverted. 5. The very first bit of the resulting byte array
580    * is inverted (because memcmp compares unsigned bytes, see property 2 above)
581    *
582    * <p>Example:
583    *
584    * <p>1234567890.1234
585    *
586    * <p>internally is represented as 3 words
587    *
588    * <p>1 234567890 123400000
589    *
590    * <p>(assuming we want a binary representation with precision=14, frac=4) in hex it's
591    *
592    * <p>00-00-00-01 0D-FB-38-D2 07-5A-EF-40
593    *
594    * <p>now, middle word is full - it stores 9 decimal digits. It goes into binary representation as
595    * is:
596    *
597    * <p>........... 0D-FB-38-D2 ............
598    *
599    * <p>First word has only one decimal digit. We can store one digit in one byte, no need to waste
600    * four:
601    *
602    * <p>01 0D-FB-38-D2 ............
603    *
604    * <p>now, last word. It's 123400000. We can store 1234 in two bytes:
605    *
606    * <p>01 0D-FB-38-D2 04-D2
607    *
608    * <p>So, we've packed 12 bytes number in 7 bytes. And now we invert the highest bit to get the
609    * final result:
610    *
611    * <p>81 0D FB 38 D2 04 D2
612    *
613    * <p>And for -1234567890.1234 it would be
614    *
615    * <p>7E F2 04 C7 2D FB 2D return a int array which represents a decimal value.
616    *
617    * @param precision precision for decimal value.
618    * @param frac fraction for decimal value.
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; // how many digits before dot
634     int wordsInt = digitsInt / digitsPerWord; // how many words to stores int part before dot.
635     int leadingDigits = digitsInt - wordsInt * digitsPerWord; // first digits
636     int wordsFrac = frac / digitsPerWord; // how many words to store int part after dot
637     int trailingDigits = frac - wordsFrac * digitsPerWord; // last digits
638 
639     // this should be one of 0, 1, 2, 3, 4
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       // TODO overflow here
674     } else if (intSize > iSizeFrom) {
675       for (; intSize > iSizeFrom; ) {
676         intSize--;
677         bin[binIdx] = mask & 0xff;
678         binIdx++;
679       }
680     }
681 
682     // when fracSize smaller than fracSizeFrom, output is truncated
683     if (fracSize < fracSizeFrom) {
684       wordsFracFrom = wordsFrac;
685       trailingDigitsFrom = trailingDigits;
686       // TODO truncated
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     // xIntFrom part
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     // wordsInt + wordsFrac part.
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   // write a word into buf.
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   /** Clears this instance. */
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     // 19 is the length of digits of Long.MAX_VALUE
821     // If a decimal can be expressed as a long value, we should use toLong method which has
822     // better performance than toBigInteger.
823     if (digitsInt + digitsFrac < 19) {
824       return new BigDecimal(BigInteger.valueOf(toLong()), digitsFrac);
825     }
826     return new BigDecimal(toBigInteger(), digitsFrac);
827   }
828 }