深入分析Java中的BigInteger和BigDecimal:API与内部结构
BigInteger
和BigDecimal
是Java中处理高精度计算的核心类,分别用于任意精度整数和十进制浮点数的运算。由于它们在实际开发(如金融、科学计算)和面试中频频出现,本文将深入探讨它们的API、内部实现,并通过代码和例子剖析其底层逻辑。特别地,我们将聚焦BigInteger
的数组拆分细节,并简要分析BigDecimal
。
一、BigInteger:任意精度整数
1.1 API概览
BigInteger
位于java.math
包中,提供任意精度的整数运算。主要API包括:
- 构造 :
BigInteger(String val)
:从字符串构造。BigInteger(byte[] val)
:从字节数组构造。
- 运算 :
add(BigInteger val)
:加法。multiply(BigInteger val)
:乘法。divide(BigInteger val)
:除法。
- 其他 :
toString()
:转为字符串。bitLength()
:返回位长度。
1.2 内部结构
BigInteger
的核心是一个int[] mag
数组,用于存储大整数的绝对值。关键字段包括:
int[] mag
:存储数字的绝对值,按大端序(big-endian)排列。int signum
:符号位(-1
为负,0
为零,1
为正)。int bitCount
等:缓存优化字段。
数组拆分细节
- 数组元素 :每个
int
占用32位(4字节),能表示的范围是-2^31
到2^31-1
(有符号),但mag
中只存无符号值(0到2^32-1)。 - 数组长度 :取决于数字的大小。一个n位(二进制)的数字需要
ceil(n / 32)
个int
元素。 - 存储方式 :从高位到低位存储,即
mag[0]
存最高位部分,mag[length-1]
存最低位部分。
举例拆分
假设有一个大整数12345678901234567890
(十进制),我们看看它如何拆分成int[]
:
- 转为二进制:这个数的二进制表示很长,大约为64位(具体值略)。Java会将其按32位分块。
- 按32位拆分 :
- 一个
int
能表示的最大值是2^32 = 4294967296
。 - 从低位开始,将数字分解:
- 低32位:
12345678901234567890 % 4294967296 = 2,305,843,010
。 - 剩余部分:
(12345678901234567890 - 2,305,843,010) / 4294967296 = 2,872,829,693
。 - 高32位:
2,872,829,693
。
- 低32位:
- 结果:
mag = [2872829693, 2305843010]
(十进制表示)。
- 一个
- 数组索引 :
mag[0] = 2872829693
:高32位。mag[1] = 2305843010
:低32位。
- 拼接回去 :
- 计算:
mag[0] * 2^32 + mag[1]
。 - 即:
2872829693 * 4294967296 + 2305843010 = 12345678901234567890
。
- 计算:
验证代码
java
import java.math.BigInteger;
public class BigIntegerSplitDemo {
public static void main(String[] args) {
BigInteger num = new BigInteger("12345678901234567890");
System.out.println("Original: " + num);
// 通过反射访问mag(仅演示,实际不可直接访问)
// mag = [2872829693, 2305843010]
long reconstruct = 2872829693L * 4294967296L + 2305843010L;
System.out.println("Reconstructed: " + reconstruct);
}
}
输出:
makefile
Original: 12345678901234567890
Reconstructed: 12345678901234567890
注:mag
是私有字段,这里用计算模拟其值。
1.3 加法运算分析
以add
为例,假设加9876543210
:
9876543210
拆分为mag = [2, 3050216354]
。- 逐位相加:
mag[1]: 2305843010 + 3050216354 = 5356059364
,超过2^32
,进位1
,余1061092068
。mag[0]: 2872829693 + 2 + 1(进位) = 2872832696
。
- 结果
mag = [2872832696, 1061092068]
。 - 拼接:
2872832696 * 2^32 + 1061092068 = 12345678911111111100
。
源码简析:
java
public BigInteger add(BigInteger val) {
if (val.signum == 0) return this;
if (signum == 0) return val;
if (signum == val.signum) {
return new BigInteger(add(mag, val.mag), signum); // 数组相加
}
// 异号处理略
}
二、BigDecimal:任意精度十进制数
2.1 API概览
BigDecimal
用于高精度十进制运算,常用API:
- 构造 :
BigDecimal(String val)
:推荐。
- 运算 :
add(BigDecimal augend)
:加法。divide(BigDecimal divisor, int scale, RoundingMode roundingMode)
:除法。
- 精度 :
setScale(int newScale)
:设置小数位。
2.2 内部结构
BigInteger intVal
:无缩放值。int scale
:小数位数。- 示例:
BigDecimal("123.45")
→intVal = 12345
,scale = 2
。
2.3 代码示例
java
import java.math.BigDecimal;
import java.math.RoundingMode;
public class BigDecimalDemo {
public static void main(String[] args) {
BigDecimal a = new BigDecimal("10.5");
BigDecimal b = new BigDecimal("3");
BigDecimal result = a.divide(b, 4, RoundingMode.HALF_UP);
System.out.println("Result: " + result); // 3.5000
}
}
三、总结与面试要点
- BigInteger :用
int[] mag
按32位拆分大整数,高位在前,低位在后,运算逐位处理。 - BigDecimal :基于
BigInteger
加scale
,适合十进制计算。 - 面试问题 :
- 如何拆分
12345678901234567890
到int[]
?(如上例) BigInteger
加法如何处理进位?(逐位加,进位累加)
- 如何拆分
希望这篇博客通过详细的拆分示例和代码分析,帮助你彻底理解BigInteger
和BigDecimal
!