All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.tikv.common.codec.MyDecimal Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2017 PingCAP, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.tikv.common.codec;

import com.google.common.annotations.VisibleForTesting;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Arrays;

// TODO: We shouldn't allow empty MyDecimal
// TODO: It seems MyDecimal to BigDecimal is very slow
public class MyDecimal implements Serializable {
  // how many digits that a word has
  private static final int digitsPerWord = 9;
  // MyDecimal can holds at most 9 words.
  private static final int wordBufLen = 9;
  // A word is 4 bytes int
  private static final int wordSize = 4;
  private static final int ten0 = 1;
  private static final int ten1 = 10;
  private static final int ten2 = 100;
  private static final int ten3 = 1000;
  private static final int ten4 = 10000;
  private static final int ten5 = 100000;
  private static final int ten6 = 1000000;
  private static final int ten7 = 10000000;
  private static final int ten8 = 100000000;
  private static final int ten9 = 1000000000;
  private static final int digMask = ten8;
  private static final int wordBase = ten9;
  private static final BigInteger wordBaseBigInt = BigInteger.valueOf(ten9);
  private static final int wordMax = wordBase - 1;
  private static final int[] div9 =
      new int[] {
        0, 0, 0, 0, 0, 0, 0, 0, 0,
        1, 1, 1, 1, 1, 1, 1, 1, 1,
        2, 2, 2, 2, 2, 2, 2, 2, 2,
        3, 3, 3, 3, 3, 3, 3, 3, 3,
        4, 4, 4, 4, 4, 4, 4, 4, 4,
        5, 5, 5, 5, 5, 5, 5, 5, 5,
        6, 6, 6, 6, 6, 6, 6, 6, 6,
        7, 7, 7, 7, 7, 7, 7, 7, 7,
        8, 8, 8, 8, 8, 8, 8, 8, 8,
        9, 9, 9, 9, 9, 9, 9, 9, 9,
        10, 10, 10, 10, 10, 10, 10, 10, 10,
        11, 11, 11, 11, 11, 11, 11, 11, 11,
        12, 12, 12, 12, 12, 12, 12, 12, 12,
        13, 13, 13, 13, 13, 13, 13, 13, 13,
        14, 14,
      };
  private static final int[] powers10 =
      new int[] {ten0, ten1, ten2, ten3, ten4, ten5, ten6, ten7, ten8, ten9};

  private static final BigInteger[] powers10BigInt =
      new BigInteger[] {
        BigInteger.valueOf(ten0),
        BigInteger.valueOf(ten1),
        BigInteger.valueOf(ten2),
        BigInteger.valueOf(ten3),
        BigInteger.valueOf(ten4),
        BigInteger.valueOf(ten5),
        BigInteger.valueOf(ten6),
        BigInteger.valueOf(ten7),
        BigInteger.valueOf(ten8),
        BigInteger.valueOf(ten9)
      };

  // A MyDecimal holds 9 words.
  private static final int maxWordBufLen = 9;
  private static final int maxFraction = 30;
  private static final int[] dig2bytes = new int[] {0, 1, 1, 2, 2, 3, 3, 4, 4, 4};

  // The following are fields of MyDecimal
  private int digitsInt;
  private int digitsFrac;
  private boolean negative;
  private int[] wordBuf = new int[maxWordBufLen];

  public MyDecimal() {}

  public MyDecimal(int digitsInt, int digitsFrac, boolean negative, int[] wordBuf) {
    this.digitsInt = digitsInt;
    this.digitsFrac = digitsFrac;
    this.negative = negative;
    this.wordBuf = wordBuf;
  }

  /**
   * Reads a word from a array at given size.
   *
   * @param b b is source data of unsigned byte as int[]
   * @param size is word size which can be used in switch statement.
   * @param start start indicates the where start to read.
   */
  @VisibleForTesting
  public static int readWord(int[] b, int size, int start) {
    int x = 0;
    switch (size) {
      case 1:
        x = (byte) b[start];
        break;
      case 2:
        x = (((byte) b[start]) << 8) + (b[start + 1] & 0xFF);
        break;
      case 3:
        int sign = b[start] & 128;
        if (sign > 0) {
          x = 0xFF << 24 | (b[start] << 16) | (b[start + 1] << 8) | (b[start + 2]);
        } else {
          x = b[start] << 16 | (b[start + 1] << 8) | b[start + 2];
        }
        break;
      case 4:
        x = b[start + 3] + (b[start + 2] << 8) + (b[start + 1] << 16) + (b[start] << 24);
        break;
    }
    return x;
  }

  /*
   * Returns total precision of this decimal. Basically, it is sum of digitsInt and digitsFrac. But there
   * are some special cases need to be token care of such as 000.001.
   * Precision reflects the actual effective precision without leading zero
   */
  public int precision() {
    int frac = this.digitsFrac;
    int digitsInt =
        this.removeLeadingZeros()[
            1]; /*this function return an array and the second element is digitsInt*/
    int precision = digitsInt + frac;
    // if no precision, it is just 0.
    if (precision == 0) {
      precision = 1;
    }
    return precision;
  }

  /**
   * Returns fraction digits that counts how many digits after ".". frac() reflects the actual
   * effective fraction without trailing zero
   */
  public int frac() {
    return digitsFrac;
  }

  /**
   * Parses a decimal value from a string
   *
   * @param value an double value
   */
  public void fromDecimal(double value) {
    String s = Double.toString(value);
    this.fromString(s);
  }

  /**
   * Parses a decimal from binary string for given precision and frac.
   *
   * @param precision precision specifies total digits that this decimal will be..
   * @param frac frac specifies how many fraction digits
   * @param bin bin is binary string which represents a decimal value.
   */
  public int fromBin(int precision, int frac, int[] bin) {
    if (bin.length == 0) {
      throw new IllegalArgumentException("Bad Float Number to parse");
    }

    int _digitsInt = precision - frac;
    int _wordsInt = _digitsInt / digitsPerWord;
    int _leadingDigits = _digitsInt - _wordsInt * digitsPerWord;
    int _wordsFrac = frac / digitsPerWord;
    int trailingDigits = frac - _wordsFrac * digitsPerWord;
    int wordsIntTo = _wordsInt;
    if (_leadingDigits > 0) {
      wordsIntTo++;
    }
    int wordsFracTo = _wordsFrac;
    if (trailingDigits > 0) {
      wordsFracTo++;
    }

    int binIdx = 0;
    int mask = -1;
    int sign = bin[binIdx] & 0x80;
    if (sign > 0) {
      mask = 0;
    }
    int binSize = decimalBinSize(precision, frac);
    int[] dCopy;
    dCopy = Arrays.copyOf(bin, binSize);
    dCopy[0] ^= 0x80;
    bin = dCopy;

    int oldWordsIntTo = wordsIntTo;
    boolean overflow = false;
    boolean truncated = false;
    if (wordsIntTo + wordsFracTo > wordBufLen) {
      if (wordsIntTo > wordBufLen) {
        wordsIntTo = wordBufLen;
        wordsFracTo = 0;
        overflow = true;
      } else {
        wordsIntTo = _wordsInt;
        wordsFracTo = wordBufLen - _wordsInt;
        truncated = true;
      }
    }

    if (overflow || truncated) {
      if (wordsIntTo < oldWordsIntTo) {
        binIdx += dig2bytes[_leadingDigits] + (_wordsInt - wordsIntTo) * wordSize;
      } else {
        trailingDigits = 0;
        _wordsFrac = wordsFracTo;
      }
    }

    this.negative = mask != 0;
    this.digitsInt = (byte) (_wordsInt * digitsPerWord + _leadingDigits);
    this.digitsFrac = (byte) (_wordsFrac * digitsPerWord + trailingDigits);

    int wordIdx = 0;
    if (_leadingDigits > 0) {
      int i = dig2bytes[_leadingDigits];
      int x = readWord(bin, i, binIdx);
      binIdx += i;
      this.wordBuf[wordIdx] = (x ^ mask) > 0 ? x ^ mask : (x ^ mask) & 0xFF;
      if (this.wordBuf[wordIdx] >= powers10[_leadingDigits + 1]) {
        throw new IllegalArgumentException("BadNumber");
      }
      if (this.wordBuf[wordIdx] != 0) {
        wordIdx++;
      } else {
        this.digitsInt -= _leadingDigits;
      }
    }
    for (int stop = binIdx + _wordsInt * wordSize; binIdx < stop; binIdx += wordSize) {
      this.wordBuf[wordIdx] = (readWord(bin, 4, binIdx) ^ mask);
      if (this.wordBuf[wordIdx] > wordMax) {
        throw new IllegalArgumentException("BadNumber");
      }
      if (wordIdx > 0 || this.wordBuf[wordIdx] != 0) {
        wordIdx++;
      } else {
        this.digitsInt -= digitsPerWord;
      }
    }

    for (int stop = binIdx + _wordsFrac * wordSize; binIdx < stop; binIdx += wordSize) {
      int x = readWord(bin, 4, binIdx);
      this.wordBuf[wordIdx] = (x ^ mask) > 0 ? x ^ mask : (x ^ mask) & 0xFF;
      if (this.wordBuf[wordIdx] > wordMax) {
        throw new IllegalArgumentException("BadNumber");
      }
      wordIdx++;
    }

    if (trailingDigits > 0) {
      int i = dig2bytes[trailingDigits];
      int x = readWord(bin, i, binIdx);
      this.wordBuf[wordIdx] =
          ((x ^ mask) > 0 ? x ^ mask : (x ^ mask) & 0xFF)
              * powers10[digitsPerWord - trailingDigits];
      if (this.wordBuf[wordIdx] > wordMax) {
        throw new IllegalArgumentException("BadNumber");
      }
    }

    return binSize;
  }

  /** Truncates any prefix zeros such as 00.001. After this, digitsInt is truncated from 2 to 0. */
  private int[] removeLeadingZeros() {
    int wordIdx = 0;
    int digitsInt = this.digitsInt;
    int i = ((digitsInt - 1) % digitsPerWord) + 1;
    for (; digitsInt > 0 && this.wordBuf[wordIdx] == 0; ) {
      digitsInt -= i;
      i = digitsPerWord;
      wordIdx++;
    }
    if (digitsInt > 0) {
      digitsInt -= countLeadingZeroes((digitsInt - 1) % digitsPerWord, this.wordBuf[wordIdx]);
    } else {
      digitsInt = 0;
    }
    int[] res = new int[2];
    res[0] = wordIdx;
    res[1] = digitsInt;
    return res;
  }

  /**
   * Counts the number of digits of prefix zeroes. For 00.001, it returns two.
   *
   * @param i i is index for getting powers10.
   * @param word word is a integer.
   */
  private int countLeadingZeroes(int i, int word) {
    int leading = 0;
    for (; word < powers10[i]; ) {
      i--;
      leading++;
    }
    return leading;
  }

  /** Returns size of word for a give value with number of digits */
  private int digitsToWords(int digits) {
    if ((digits + digitsPerWord - 1) >= 0 && ((digits + digitsPerWord - 1) < 128)) {
      return div9[digits + digitsPerWord - 1];
    }
    return (digits + digitsPerWord - 1) / digitsPerWord;
  }

  /**
   * parser a decimal value from a string.
   *
   * @param s s is a decimal in string form.
   */
  @VisibleForTesting
  public void fromString(String s) {
    char[] sCharArray = s.toCharArray();
    fromCharArray(sCharArray);
  }

  // helper function for fromString
  private void fromCharArray(char[] str) {
    int startIdx = 0;
    // found first character is not space and start from here
    for (; startIdx < str.length; startIdx++) {
      if (!Character.isSpaceChar(str[startIdx])) {
        break;
      }
    }

    if (str.length == 0) {
      throw new IllegalArgumentException("BadNumber");
    }

    // skip sign and record where digits start from
    // [-, 1, 2, 3]
    // [+, 1, 2, 3]
    // for +/-, we need skip them and record sign information into negative field.
    switch (str[startIdx]) {
      case '-':
        this.negative = true;
        startIdx++;
        break;
      case '+':
        startIdx++;
        break;
    }
    int strIdx = startIdx;
    for (; strIdx < str.length && Character.isDigit(str[strIdx]); ) {
      strIdx++;
    }
    // we initialize strIdx in case of sign notation, here we need subtract startIdx from strIdx
    // cause strIdx is used for counting the number of digits.
    int digitsInt = strIdx - startIdx;
    int digitsFrac;
    int endIdx;
    if (strIdx < str.length && str[strIdx] == '.') {
      endIdx = strIdx + 1;
      // detect where is the end index of this char array.
      for (; endIdx < str.length && Character.isDigit(str[endIdx]); ) {
        endIdx++;
      }
      digitsFrac = endIdx - strIdx - 1;
    } else {
      digitsFrac = 0;
    }

    if (digitsInt + digitsFrac == 0) {
      throw new IllegalArgumentException("BadNumber");
    }
    int wordsInt = digitsToWords(digitsInt);
    int wordsFrac = digitsToWords(digitsFrac);

    // TODO the following code are fixWordCntError such as overflow and truncated error
    boolean overflow = false;
    boolean truncated = false;
    if (wordsInt + wordsFrac > wordBufLen) {
      if (wordsInt > wordBufLen) {
        wordsInt = wordBufLen;
        wordsFrac = 0;
        overflow = true;
      } else {
        wordsFrac = wordBufLen - wordsInt;
        truncated = true;
      }
    }

    if (overflow || truncated) {
      digitsFrac = wordsFrac * digitsPerWord;
      if (overflow) {
        digitsInt = wordsInt * digitsPerWord;
      }
    }
    this.digitsInt = digitsInt;
    this.digitsFrac = digitsFrac;
    int wordIdx = wordsInt;
    int strIdxTmp = strIdx;
    int word = 0;
    int innerIdx = 0;
    for (; digitsInt > 0; ) {
      digitsInt--;
      strIdx--;
      word += (str[strIdx] - '0') * powers10[innerIdx];
      innerIdx++;
      if (innerIdx == digitsPerWord) {
        wordIdx--;
        this.wordBuf[wordIdx] = word;
        word = 0;
        innerIdx = 0;
      }
    }

    if (innerIdx != 0) {
      wordIdx--;
      this.wordBuf[wordIdx] = word;
    }

    wordIdx = wordsInt;
    strIdx = strIdxTmp;
    word = 0;
    innerIdx = 0;

    for (; digitsFrac > 0; ) {
      digitsFrac--;
      strIdx++;
      word = (str[strIdx] - '0') + word * 10;
      innerIdx++;
      if (innerIdx == digitsPerWord) {
        this.wordBuf[wordIdx] = word;
        wordIdx++;
        word = 0;
        innerIdx = 0;
      }
    }
    if (innerIdx != 0) {
      this.wordBuf[wordIdx] = word * powers10[digitsPerWord - innerIdx];
    }

    // this is -0000 is just 0.
    boolean allZero = true;
    for (int i = 0; i < wordBufLen; i++) {
      if (this.wordBuf[i] != 0) {
        allZero = false;
        break;
      }
    }
    if (allZero) {
      this.negative = false;
    }
  }

  // Returns a decimal string.
  @Override
  public String toString() {
    char[] str;

    int _digitsFrac = this.digitsFrac;
    int[] res = removeLeadingZeros();
    int wordStartIdx = res[0];
    int digitsInt = res[1];
    if (digitsInt + _digitsFrac == 0) {
      digitsInt = 1;
      wordStartIdx = 0;
    }

    int digitsIntLen = digitsInt;
    if (digitsIntLen == 0) {
      digitsIntLen = 1;
    }
    int digitsFracLen = _digitsFrac;
    int length = digitsIntLen + digitsFracLen;
    if (this.negative) {
      length++;
    }
    if (_digitsFrac > 0) {
      length++;
    }
    str = new char[length];

    int strIdx = 0;
    if (this.negative) {
      str[strIdx] = '-';
      strIdx++;
    }

    if (_digitsFrac > 0) {
      int fracIdx = strIdx + digitsIntLen;
      int wordIdx = wordStartIdx + digitsToWords(digitsInt);
      str[fracIdx] = '.';
      fracIdx++;
      for (; _digitsFrac > 0; _digitsFrac -= digitsPerWord) {
        int x = this.wordBuf[wordIdx];
        wordIdx++;
        for (int i = Math.min(_digitsFrac, MyDecimal.digitsPerWord); i > 0; i--) {
          int y = x / digMask;
          str[fracIdx] = (char) ((char) y + '0');
          fracIdx++;
          x -= y * digMask;
          x *= 10;
        }
      }
    }
    if (digitsInt > 0) {
      strIdx += digitsInt;
      int wordIdx = wordStartIdx + digitsToWords(digitsInt);
      for (; digitsInt > 0; digitsInt -= digitsPerWord) {
        wordIdx--;
        int x = this.wordBuf[wordIdx];
        for (int i = Math.min(digitsInt, MyDecimal.digitsPerWord); i > 0; i--) {
          int temp = x / 10;
          strIdx--;
          str[strIdx] = (char) ('0' + (x - temp * 10));
          x = temp;
        }
      }
    } else {
      str[strIdx] = '0';
    }

    return new String(str);
  }

  // decimalBinSize returns the size of array to hold a binary representation of a decimal.
  private int decimalBinSize(int precision, int frac) {
    int digitsInt = precision - frac;
    int wordsInt = digitsInt / digitsPerWord;
    int wordsFrac = frac / digitsPerWord;
    int xInt = digitsInt - wordsInt * digitsPerWord;
    int xFrac = frac - wordsFrac * digitsPerWord;
    return wordsInt * wordSize + dig2bytes[xInt] + wordsFrac * wordSize + dig2bytes[xFrac];
  }

  /**
   * ToBin converts decimal to its binary fixed-length representation two representations of the
   * same length can be compared with memcmp with the correct -1/0/+1 result
   *
   * 

PARAMS precision/frac - if precision is 0, internal value of the decimal will be used, then * the encoded value is not memory comparable. * *

NOTE the buffer is assumed to be of the size decimalBinSize(precision, frac) * *

RETURN VALUE bin - binary value errCode - eDecOK/eDecTruncate/eDecOverflow * *

DESCRIPTION for storage decimal numbers are converted to the "binary" format. * *

This format has the following properties: 1. length of the binary representation depends on * the {precision, frac} as provided by the caller and NOT on the digitsInt/digitsFrac of the * decimal to convert. 2. binary representations of the same {precision, frac} can be compared * with memcmp - with the same result as DecimalCompare() of the original decimals (not taking * into account possible precision loss during conversion). * *

This binary format is as follows: 1. First the number is converted to have a requested * precision and frac. 2. Every full digitsPerWord digits of digitsInt part are stored in 4 bytes * as is 3. The first digitsInt % digitsPerWord digits are stored in the reduced number of bytes * (enough bytes to store this number of digits - see dig2bytes) 4. same for frac - full word are * stored as is, the last frac % digitsPerWord digits - in the reduced number of bytes. 5. If the * number is negative - every byte is inverted. 5. The very first bit of the resulting byte array * is inverted (because memcmp compares unsigned bytes, see property 2 above) * *

Example: * *

1234567890.1234 * *

internally is represented as 3 words * *

1 234567890 123400000 * *

(assuming we want a binary representation with precision=14, frac=4) in hex it's * *

00-00-00-01 0D-FB-38-D2 07-5A-EF-40 * *

now, middle word is full - it stores 9 decimal digits. It goes into binary representation as * is: * *

........... 0D-FB-38-D2 ............ * *

First word has only one decimal digit. We can store one digit in one byte, no need to waste * four: * *

01 0D-FB-38-D2 ............ * *

now, last word. It's 123400000. We can store 1234 in two bytes: * *

01 0D-FB-38-D2 04-D2 * *

So, we've packed 12 bytes number in 7 bytes. And now we invert the highest bit to get the * final result: * *

81 0D FB 38 D2 04 D2 * *

And for -1234567890.1234 it would be * *

7E F2 04 C7 2D FB 2D return a int array which represents a decimal value. * * @param precision precision for decimal value. * @param frac fraction for decimal value. */ public int[] toBin(int precision, int frac) { if (precision > digitsPerWord * maxWordBufLen || precision < 0 || frac > maxFraction || frac < 0) { throw new IllegalArgumentException("BadNumber"); } int mask = 0; if (this.negative) { mask = -1; } int digitsInt = precision - frac; // how many digits before dot int wordsInt = digitsInt / digitsPerWord; // how many words to stores int part before dot. int leadingDigits = digitsInt - wordsInt * digitsPerWord; // first digits int wordsFrac = frac / digitsPerWord; // how many words to store int part after dot int trailingDigits = frac - wordsFrac * digitsPerWord; // last digits // this should be one of 0, 1, 2, 3, 4 int wordsFracFrom = this.digitsFrac / digitsPerWord; int trailingDigitsFrom = this.digitsFrac - wordsFracFrom * digitsPerWord; int intSize = wordsInt * wordSize + dig2bytes[leadingDigits]; int fracSize = wordsFrac * wordSize + dig2bytes[trailingDigits]; int fracSizeFrom = wordsFracFrom * wordSize + dig2bytes[trailingDigitsFrom]; int originIntSize = intSize; int originFracSize = fracSize; int[] bin = new int[intSize + fracSize]; int binIdx = 0; int[] res = this.removeLeadingZeros(); int wordIdxFrom = res[0]; int digitsIntFrom = res[1]; if (digitsIntFrom + fracSizeFrom == 0) { mask = 0; digitsInt = 1; } int wordsIntFrom = digitsIntFrom / digitsPerWord; int leadingDigitsFrom = digitsIntFrom - wordsIntFrom * digitsPerWord; int iSizeFrom = wordsIntFrom * wordSize + dig2bytes[leadingDigitsFrom]; if (digitsInt < digitsIntFrom) { wordIdxFrom += (wordsIntFrom - wordsInt); if (leadingDigitsFrom > 0) { wordIdxFrom++; } if (leadingDigits > 0) { wordIdxFrom--; } wordsIntFrom = wordsInt; leadingDigitsFrom = leadingDigits; // TODO overflow here } else if (intSize > iSizeFrom) { for (; intSize > iSizeFrom; ) { intSize--; bin[binIdx] = mask & 0xff; binIdx++; } } // when fracSize smaller than fracSizeFrom, output is truncated if (fracSize < fracSizeFrom) { wordsFracFrom = wordsFrac; trailingDigitsFrom = trailingDigits; // TODO truncated } else if (fracSize > fracSizeFrom && trailingDigitsFrom > 0) { if (wordsFrac == wordsFracFrom) { trailingDigitsFrom = trailingDigits; fracSize = fracSizeFrom; } else { wordsFracFrom++; trailingDigitsFrom = 0; } } // xIntFrom part if (leadingDigitsFrom > 0) { int i = dig2bytes[leadingDigitsFrom]; int x = (this.wordBuf[wordIdxFrom] % powers10[leadingDigitsFrom]) ^ mask; wordIdxFrom++; writeWord(bin, x, i, binIdx); binIdx += i; } // wordsInt + wordsFrac part. for (int stop = wordIdxFrom + wordsIntFrom + wordsFracFrom; wordIdxFrom < stop; binIdx += wordSize) { int x = this.wordBuf[wordIdxFrom] ^ mask; wordIdxFrom++; writeWord(bin, x, 4, binIdx); } if (trailingDigitsFrom > 0) { int x; int i = dig2bytes[trailingDigitsFrom]; int lim = trailingDigits; if (wordsFracFrom < wordsFrac) { lim = digitsPerWord; } for (; trailingDigitsFrom < lim && dig2bytes[trailingDigitsFrom] == i; ) { trailingDigitsFrom++; } x = (this.wordBuf[wordIdxFrom] / powers10[digitsPerWord - trailingDigitsFrom]) ^ mask; writeWord(bin, x, i, binIdx); binIdx += i; } if (fracSize > fracSizeFrom) { int binIdxEnd = originIntSize + originFracSize; for (; fracSize > fracSizeFrom && binIdx < binIdxEnd; ) { fracSize--; bin[binIdx] = mask & 0xff; binIdx++; } } bin[0] ^= 0x80; return bin; } // write a word into buf. private void writeWord(int[] b, int word, int size, int start) { switch (size) { case 1: b[start] = word & 0xFF; break; case 2: b[start] = (word >>> 8) & 0xFF; b[start + 1] = word & 0xFF; break; case 3: b[start] = (word >>> 16) & 0xFF; b[start + 1] = (word >>> 8) & 0xFF; b[start + 2] = word & 0xFF; break; case 4: b[start] = (word >>> 24) & 0xFF; b[start + 1] = (word >>> 16) & 0xFF; b[start + 2] = (word >>> 8) & 0xFF; b[start + 3] = word & 0xFF; break; } } /** Clears this instance. */ public void clear() { this.digitsFrac = 0; this.digitsInt = 0; this.negative = false; } private BigInteger toBigInteger() { BigInteger x = BigInteger.ZERO; int wordIdx = 0; for (int i = this.digitsInt; i > 0; i -= digitsPerWord) { x = x.multiply(wordBaseBigInt).add(BigInteger.valueOf(this.wordBuf[wordIdx])); wordIdx++; } for (int i = this.digitsFrac; i > 0; i -= digitsPerWord) { x = x.multiply(wordBaseBigInt).add(BigInteger.valueOf(this.wordBuf[wordIdx])); wordIdx++; } if (digitsFrac % digitsPerWord != 0) { x = x.divide(powers10BigInt[digitsPerWord - digitsFrac % digitsPerWord]); } if (negative) { x = x.negate(); } return x; } public long toLong() { long x = 0; int wordIdx = 0; for (int i = this.digitsInt; i > 0; i -= digitsPerWord) { x = x * wordBase + this.wordBuf[wordIdx]; wordIdx++; } for (int i = this.digitsFrac; i > 0; i -= digitsPerWord) { x = x * wordBase + this.wordBuf[wordIdx]; wordIdx++; } if (digitsFrac % digitsPerWord != 0) { x = x / powers10[digitsPerWord - digitsFrac % digitsPerWord]; } if (negative) { x = -x; } return x; } public BigDecimal toBigDecimal() { // 19 is the length of digits of Long.MAX_VALUE // If a decimal can be expressed as a long value, we should use toLong method which has // better performance than toBigInteger. if (digitsInt + digitsFrac < 19) { return new BigDecimal(BigInteger.valueOf(toLong()), digitsFrac); } return new BigDecimal(toBigInteger(), digitsFrac); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy