BigDecimal
是 Java 中 java.math
包提供的高精度十进制浮点数类,专为解决基本类型(float
/double
)的精度缺陷而设计,广泛用于金融、科学计算等对精度要求极高的场景。以下从核心特性、使用方法、常见问题对比、注意事项等方面详细介绍:
一、核心特性
任意精度
不像double
(64 位,约 15-17 位有效数字)有固定精度限制,BigDecimal
可通过MathContext
或setScale
自定义精度,理论上支持无限精度(仅受内存限制)。精确十进制表示
double
是二进制浮点数,无法精确表示如0.1
这样的十进制小数(会存储为近似值),而BigDecimal
基于十进制算术,可精确存储和计算小数。可控舍入模式
支持 8 种舍入规则(如四舍五入、向上取整、向下取整等),满足不同场景的精度控制需求(如货币计算需精确到分)。超大数值范围
突破double
的范围限制(double
最大约1.8×10³⁰⁸
),可表示极大或极小的数(如10^10000
或10^-10000
)。
二、基本使用方法
1. 初始化(关键!避免精度陷阱)
BigDecimal
有多种构造方法,强烈推荐用字符串初始化,避免因 double
本身的精度误差导致问题:
import java.math.BigDecimal;// 正确方式:字符串构造(无精度损失)
BigDecimal num1 = new BigDecimal("0.1"); // 精确表示0.1
BigDecimal num2 = new BigDecimal("100.0000000001"); // 精确存储多位小数// 错误方式:double构造(可能引入精度误差)
BigDecimal bad1 = new BigDecimal(0.1); // 实际存储的是0.1的二进制近似值(约0.1000000000000000055...)
BigDecimal bad2 = new BigDecimal(1.234567890123456789); // 超过double精度,会被截断// 其他方式:整数或长整数构造
BigDecimal num3 = new BigDecimal(123); // 等价于new BigDecimal("123")
BigDecimal num4 = new BigDecimal(1234567890123456789L); // 大整数
2. 核心运算方法
BigDecimal
不支持 +
、-
、*
、/
等运算符,需通过以下方法进行运算,且运算结果需用新变量接收(原对象不变):
方法 | 功能 | 示例(num1=0.1,num2=0.2) |
---|---|---|
add(BigDecimal) | 加法 | num1.add(num2) → 0.3 |
subtract(BigDecimal) | 减法 | num2.subtract(num1) → 0.1 |
multiply(BigDecimal) | 乘法 | num1.multiply(num2) → 0.02 |
divide(BigDecimal) | 除法(需指定舍入模式) | num1.divide(num2, 2, RoundingMode.HALF_UP) → 0.50 |
pow(int) | 幂运算 | num1.pow(3) → 0.001(0.1³) |
3. 精度与舍入控制
(1)设置小数位数(setScale
)
BigDecimal num = new BigDecimal("3.1415926");// 保留2位小数,四舍五入(RoundingMode.HALF_UP)
BigDecimal scaled1 = num.setScale(2, RoundingMode.HALF_UP); // 3.14// 保留4位小数,向上取整(RoundingMode.UP)
BigDecimal scaled2 = num.setScale(4, RoundingMode.UP); // 3.1416(第5位是9,向上进1)// 保留1位小数,向下取整(RoundingMode.DOWN)
BigDecimal scaled3 = num.setScale(1, RoundingMode.DOWN); // 3.1(直接截断)
(2)常用舍入模式(RoundingMode
)
模式 | 说明 | 示例(保留 1 位小数) |
---|---|---|
HALF_UP | 四舍五入(最常用) | 3.14 → 3.1;3.15 → 3.2 |
UP | 向上取整(远离零) | 3.11 → 3.2;-3.11 → -3.2 |
DOWN | 向下取整(趋向零) | 3.19 → 3.1;-3.19 → -3.1 |
HALF_DOWN | 五舍六入 | 3.15 → 3.1;3.16 → 3.2 |
CEILING | 向正无穷取整 | 3.1 → 4.0;-3.1 → -3.0 |
FLOOR | 向负无穷取整 | 3.1 → 3.0;-3.1 → -4.0 |
4. 比较大小(compareTo
)
不能用 ==
或 equals
比较(equals
会严格比较精度,如 0.1
与 0.10
视为不等),需用 compareTo
:
BigDecimal a = new BigDecimal("0.1");
BigDecimal b = new BigDecimal("0.10");
BigDecimal c = new BigDecimal("0.2");// compareTo 返回值:
// 0 → 相等;1 → a > b;-1 → a < b
System.out.println(a.compareTo(b)); // 0(数值相等,忽略精度差异)
System.out.println(a.compareTo(c)); // -1(0.1 < 0.2)
System.out.println(c.compareTo(a)); // 1(0.2 > 0.1)// 错误:equals 会比较精度(0.1 与 0.10 精度不同,返回false)
System.out.println(a.equals(b)); // false
5. 转换为其他类型
BigDecimal num = new BigDecimal("123.456");// 转换为基本类型(可能溢出,需谨慎)
double d = num.doubleValue(); // 123.456(可能损失精度)
long l = num.longValue(); // 123(截断小数部分)// 转换为字符串(推荐,无精度损失)
String s = num.toString(); // "123.456"
三、与 double
的对比(解决哪些问题?)
场景 | double 问题 | BigDecimal 解决方案 |
---|---|---|
0.1 + 0.2 | 结果为 0.30000000000000004 (精度损失) | 结果为 0.3 (精确计算) |
货币计算(如分) | 无法精确表示分(如 0.01 存储为近似值) | 可精确表示 0.01 ,避免金额误差 |
超大 / 超小数值 | 超过范围会溢出为 Infinity 或 0 | 可表示任意大小数值(受内存限制) |
舍入规则控制 | 仅支持默认舍入,无法自定义 | 8 种舍入模式,灵活控制精度 |
四、注意事项(避坑指南)
初始化必须用字符串
用double
构造BigDecimal
会继承double
的精度误差(如new BigDecimal(0.1)
实际是0.1000000000000000055...
),务必用new BigDecimal("0.1")
。除法必须指定舍入模式
当除法无法整除时(如1 ÷ 3
),不指定舍入模式会抛出ArithmeticException
:// 错误:1 ÷ 3 无法整除,无舍入模式会抛异常 new BigDecimal("1").divide(new BigDecimal("3")); // 正确:指定舍入模式和精度 new BigDecimal("1").divide(new BigDecimal("3"), 2, RoundingMode.HALF_UP); // 0.33
避免频繁创建对象
BigDecimal
是不可变对象(运算后生成新对象),频繁运算会产生大量临时对象,影响性能。可重用对象或使用MathContext
控制精度。谨慎处理
equals
方法equals
会比较数值和精度(如0.1
与0.10
视为不等),比较数值相等需用compareTo
。性能权衡
BigDecimal
运算效率远低于double
,非高精度场景(如普通科学计算)无需使用。
五、典型应用场景
- 金融 / 货币计算:如银行转账、税费计算(需精确到分,避免一分钱误差)。
- 高精度科学计算:如物理、数学中的精确数值模拟(需保留多位有效数字)。
- 超大数值处理:如密码学中的大整数运算、天文数据(超过
double
范围)。