Java大数处理:BigInteger与BigDecimal
文章目录
- Java大数处理:BigInteger与BigDecimal
- 1. 引入:为什么需要大数?
- 1.1 基本数据类型的局限
- 1.2 解决方案:大数类
- 2. BigInteger:超大整数
- 2.1 创建BigInteger对象
- 2.2 基本运算
- 2.3 比较和判断
- 2.4 其他常用方法
- 2.5 实际应用:计算阶乘
- 3. BigDecimal:高精度小数
- 3.1 为什么需要BigDecimal?
- 3.2 创建BigDecimal对象
- 3.3 基本运算
- 3.4 除法与舍入模式
- 3.5 设置精度和比较
- 3.6 stripTrailingZeros:去除末尾零
- 4. BigInteger vs BigDecimal 对比
- 5. 大数结构的底层原理(扩展)
- 5.1 大数的存储方式
- 5.2 基本运算原理
- 5.3 为什么字符串构造更准确?
- 6. 实际应用场景
- 6.1 金额计算
- 6.2 大数阶乘
- 6.3 高精度开方/幂
- 7. 易错点总结
- 8. 综合总结
Java大数处理:BigInteger与BigDecimal
1. 引入:为什么需要大数?
1.1 基本数据类型的局限
在Java中,基本数据类型有固定的取值范围:
| 类型 | 位数 | 最大值 |
|---|---|---|
| int | 32位 | 约21亿(2^31-1) |
| long | 64位 | 约9.22×1018(263-1) |
| double | 64位 | 约1.8×10^308(但精度有限) |
inta=2147483647;// int最大值intb=a+1;// 溢出,变成-2147483648longc=9223372036854775807L;// long最大值longd=c+1;// 溢出,变成-9223372036854775808问题:如果需要计算天文数字(如100的阶乘、圆周率后1000位),基本类型完全不够用。
1.2 解决方案:大数类
Java提供了两个专门处理大数的类:
- BigInteger:处理超大整数
- BigDecimal:处理超高精度小数
它们可以表示任意大小的数字,只受内存限制。
2. BigInteger:超大整数
2.1 创建BigInteger对象
importjava.math.BigInteger;// 方式1:通过字符串(推荐)BigIntegernum1=newBigInteger("12345678901234567890");// 方式2:通过long(不推荐,因为long本身可能溢出)BigIntegernum2=BigInteger.valueOf(100);// 方式3:常用常量BigIntegerzero=BigInteger.ZERO;BigIntegerone=BigInteger.ONE;BigIntegerten=BigInteger.TEN;注意:BigInteger是不可变对象,所有操作都会返回新的BigInteger。
2.2 基本运算
BigIntegera=newBigInteger("100");BigIntegerb=newBigInteger("20");// 加减乘除BigIntegersum=a.add(b);// 加法:120BigIntegerdiff=a.subtract(b);// 减法:80BigIntegerproduct=a.multiply(b);// 乘法:2000BigIntegerquotient=a.divide(b);// 除法:5BigIntegerremainder=a.remainder(b);// 余数:0// 混合运算:a + b * 10 - 5BigIntegerresult=a.add(b.multiply(BigInteger.TEN)).subtract(BigInteger.valueOf(5));2.3 比较和判断
BigIntegera=newBigInteger("100");BigIntegerb=newBigInteger("200");// 比较大小:返回-1(小于), 0(等于), 1(大于)intcmp=a.compareTo(b);// -1// 判断是否相等booleanisEqual=a.equals(b);// false// 判断是否为0booleanisZero=a.equals(BigInteger.ZERO);// 取最大值/最小值BigIntegermax=a.max(b);// 200BigIntegermin=a.min(b);// 1002.4 其他常用方法
BigIntegernum=newBigInteger("12345");// 绝对值BigIntegerabs=num.abs();// 幂运算BigIntegerpower=BigInteger.valueOf(2).pow(10);// 2^10 = 1024// 取相反数BigIntegerneg=num.negate();// -12345// 转为基本类型(可能溢出)intintVal=num.intValue();// 12345,超出范围会截断longlongVal=num.longValue();// 12345Stringstr=num.toString();// "12345"// 检查是否能转为int/long而不溢出booleancanConvert=num.bitLength()<32;// 判断是否在int范围内2.5 实际应用:计算阶乘
publicstaticBigIntegerfactorial(intn){BigIntegerresult=BigInteger.ONE;for(inti=2;i<=n;i++){result=result.multiply(BigInteger.valueOf(i));}returnresult;}// 计算100的阶乘BigIntegerfact=factorial(100);System.out.println(fact);// 输出一个巨大的数(有158位)3. BigDecimal:高精度小数
3.1 为什么需要BigDecimal?
看一个double的精度问题:
doublea=0.1;doubleb=0.2;System.out.println(a+b);// 0.30000000000000004 ????因为二进制无法精确表示0.1,所以double运算会有精度损失。BigDecimal用来解决这个问题。
3.2 创建BigDecimal对象
importjava.math.BigDecimal;// 方式1:用字符串(推荐,精度准确)BigDecimalnum1=newBigDecimal("0.1");BigDecimalnum2=newBigDecimal("0.2");// 方式2:用double(不推荐,已经有精度损失)BigDecimalnum3=newBigDecimal(0.1);// 得到的是0.100000000000000005...// 方式3:用valueOf(内部用字符串,推荐)BigDecimalnum4=BigDecimal.valueOf(0.1);// 常用常量BigDecimalzero=BigDecimal.ZERO;BigDecimalone=BigDecimal.ONE;BigDecimalten=BigDecimal.TEN;重点:优先使用new BigDecimal("字符串")或BigDecimal.valueOf(),不要用new BigDecimal(double)。
3.3 基本运算
BigDecimala=newBigDecimal("10.5");BigDecimalb=newBigDecimal("3.2");BigDecimalsum=a.add(b);// 加法:13.7BigDecimaldiff=a.subtract(b);// 减法:7.3BigDecimalproduct=a.multiply(b);// 乘法:33.60// 除法需要指定舍入模式BigDecimalquotient=a.divide(b,2,RoundingMode.HALF_UP);// 3.28,保留2位,四舍五入3.4 除法与舍入模式
除法最容易出问题,因为可能产生无限小数:
BigDecimala=newBigDecimal("10");BigDecimalb=newBigDecimal("3");// 不指定精度会抛异常(ArithmeticException)// BigDecimal c = a.divide(b); // 无限小数,报错// 正确用法:指定小数位数和舍入模式BigDecimalc=a.divide(b,2,RoundingMode.HALF_UP);// 3.33常用舍入模式:
| 舍入模式 | 说明 | 例子(保留2位) |
|---|---|---|
HALF_UP | 四舍五入 | 3.135 → 3.14 |
HALF_DOWN | 五舍六入 | 3.135 → 3.13 |
CEILING | 向上取整 | 3.131 → 3.14 |
FLOOR | 向下取整 | 3.139 → 3.13 |
UP | 远离零 | 3.131 → 3.14,-3.131 → -3.14 |
DOWN | 接近零 | 3.139 → 3.13 |
3.5 设置精度和比较
BigDecimala=newBigDecimal("10.555");BigDecimalb=newBigDecimal("10.5550");// 设置小数位数BigDecimalrounded=a.setScale(2,RoundingMode.HALF_UP);// 10.56// 比较大小(使用compareTo,不要用equals)intcmp=a.compareTo(b);// 0,因为值相同(10.555 == 10.5550)booleanisEqual=a.equals(b);// false!因为位数不同// 正确比较if(a.compareTo(b)==0){System.out.println("值相等");}重点:equals会比较小数位数,compareTo只比较数值。比较值是否相等时用compareTo。
3.6 stripTrailingZeros:去除末尾零
BigDecimalnum=newBigDecimal("10.5000");BigDecimalstripped=num.stripTrailingZeros();// 10.5System.out.println(stripped.toPlainString());// "10.5"(不用科学计数法)4. BigInteger vs BigDecimal 对比
| 维度 | BigInteger | BigDecimal |
|---|---|---|
| 用途 | 超大整数 | 高精度小数 |
| 适用场景 | 阶乘、大数运算、密码学 | 金额计算、科学计算 |
| 小数点 | 没有 | 有 |
| 除法默认 | 整除(直接除) | 需要指定精度和舍入 |
| 常见异常 | 无 | ArithmeticException(无限小数) |
| 性能 | 稍快 | 稍慢 |
5. 大数结构的底层原理(扩展)
5.1 大数的存储方式
BigInteger内部使用int[]来存储数字:
// 简化版的大数结构publicclassMyBigInteger{privateint[]mag;// 存储数字的绝对值privateintsignum;// 符号:-1(负), 0(零), 1(正)}例如,数字1234567890会被拆分成多个int存储,类似把数字按进制拆分。
5.2 基本运算原理
加法:从低位到高位逐位相加,处理进位
1234 + 5678 = 6912 (进位处理)乘法:类似手算竖式乘法,复杂度O(n²)
5.3 为什么字符串构造更准确?
// double构造:0.1在二进制中是无限循环小数,传入时已经失真newBigDecimal(0.1)→0.100000000000000005551115123125...// 字符串构造:直接按十进制解析,完全精确newBigDecimal("0.1")→0.16. 实际应用场景
6.1 金额计算
// 错误:用double算钱doubleprice=19.99;doubletax=price*0.08;// 1.5992000000000002,精度丢失// 正确:用BigDecimalBigDecimalprice=newBigDecimal("19.99");BigDecimaltax=price.multiply(newBigDecimal("0.08"));BigDecimaltotal=price.add(tax);total=total.setScale(2,RoundingMode.HALF_UP);// 保留两位小数6.2 大数阶乘
publicstaticBigIntegerfactorial(intn){BigIntegerresult=BigInteger.ONE;for(inti=2;i<=n;i++){result=result.multiply(BigInteger.valueOf(i));}returnresult;}// 计算50!,结果有65位6.3 高精度开方/幂
// 计算2的1000次方BigIntegerpower=BigInteger.valueOf(2).pow(1000);// 高精度开方BigDecimalnum=newBigDecimal("2");BigDecimalsqrt=sqrt(num,50);// 开方,保留50位小数(需要自己实现)7. 易错点总结
| 易错点 | 错误写法 | 正确写法 |
|---|---|---|
| 用double构造BigDecimal | new BigDecimal(0.1) | new BigDecimal("0.1") |
| 除法不指定精度 | a.divide(b) | a.divide(b, 2, RoundingMode.HALF_UP) |
| 用equals比较BigDecimal | a.equals(b) | a.compareTo(b) == 0 |
| 忘记BigDecimal不可变 | a.add(b)认为a变了 | a = a.add(b) |
| 用valueOf传入浮点数 | BigDecimal.valueOf(0.1) | 用字符串构造更好 |
比较BigInteger用== | a == b | a.equals(b) |
8. 综合总结
| 场景 | 推荐类型 | 原因 |
|---|---|---|
| 精确金额计算 | BigDecimal | 避免浮点精度问题 |
| 超大整数运算 | BigInteger | 任意大小整数 |
| 普通整数运算 | int/long | 性能好 |
| 普通浮点运算 | double | 性能好,不要求精度 |
| 科学计算 | double/BigDecimal | 看精度要求 |
一句话总结:
要精确用BigDecimal,要超大用BigInteger,普通场景用基本类型。
