整型数值精度问题产生的原因

  • 当整型数值进行计算时,其结果超过了它的最大长度或最小长度时(结果溢出),就会出现精度问题

    BigInteger类的解决方式

  • 首先,BigInter可以表示任意精度的整数,是因为它使用了数组的形式

    • 它会将数据通过byte[]来间接表示底层的二进制,因此只要数组足够长,就能够表示足够大精度的整数,如下
      1. byte[] num1 = { 1 }; // 二进制值:00000001 00000010
      2. byte[] num2 = { 1, 2 }; // 二进制值:00000001 00000010
      3. byte[] num3 = { 1, 2, 3 }; // 二进制值:00000001 00000010
      4. byte[] num4 = { 1, 2, 3, 4, 5 };
      5. /* num1, num2, num3 初始化BigInteger后表示的二进制分别为
      6. num1: 00000001
      7. num2: 00000001 00000010
      8. num3: 00000001 00000010 00000011
      9. num4: 00000001 00000010 00000011 00000100 00000101
      10. 注意:初始化参数虽然是byte[],但BigInteger最终会以int[]方式存储,内部参数为mag
      11. */
      12. // 十进制值:1 内部mag结果:{ 1 }
      13. BigInteger bigNum1 = new BigInteger(num1);
      14. // 十进制值:258 内部mag结果:{ 258 }
      15. BigInteger bigNum2 = new BigInteger(num2);
      16. // 十进制值:66051 内部mag结果:{ 66051 }
      17. BigInteger bigNum3 = new BigInteger(num3);
      18. // 十进制值:2^32 + 33752069 内部mag结果:{ 1, 33752069 }
      19. BigInteger bigNum4 = new BigInteger(num4);
  • 初始化BigInteger对象源码

    1. // 通过byte[]初始化一个BigInteger
    2. public BigInteger(byte[] val) {
    3. if (val.length == 0)
    4. throw new NumberFormatException("Zero length BigInteger");
    5. if (val[0] < 0) {
    6. mag = makePositive(val);
    7. signum = -1;
    8. } else {
    9. mag = stripLeadingZeroBytes(val); // 调用内部转换函数
    10. signum = (mag.length == 0 ? 0 : 1);
    11. }
    12. if (mag.length >= MAX_MAG_LENGTH) {
    13. checkRange();
    14. }
    15. }
    16. // BigInteger内部将byte[]转换成int[]记录到mag中
    17. private static int[] stripLeadingZeroBytes(byte a[]) {
    18. int byteLength = a.length;
    19. int keep;
    20. // Find first nonzero byte
    21. for (keep = 0; keep < byteLength && a[keep] == 0; keep++)
    22. ;
    23. // Allocate new array and copy relevant part of input array
    24. int intLength = ((byteLength - keep) + 3) >>> 2;
    25. int[] result = new int[intLength];
    26. int b = byteLength - 1;
    27. for (int i = intLength-1; i >= 0; i--) {
    28. result[i] = a[b--] & 0xff;
    29. int bytesRemaining = b - keep + 1;
    30. int bytesToTransfer = Math.min(3, bytesRemaining);
    31. for (int j=8; j <= (bytesToTransfer << 3); j += 8)
    32. result[i] |= ((a[b--] & 0xff) << j);
    33. }
    34. return result;
    35. }
  • 进行计算时,是以模拟十进制的方式进行计算的

    • 加法:从最低位开始,每位相加,能进位则进位
    • 乘法:从最低位开始,每位与另一个数相乘,最后结果相加
    • 其他同理

      参考

  • Java的BigDecimal如何解决浮点数精度问题