BigDecimal操作手册
BigDecimal 是 Java 中用于高精度计算的类,特别适用于财务计算和需要精确小数运算的场景。它克服了 float 和 double 类型可能出现的精度问题。
Java BigDecimal 操作手册
一、概述
BigDecimal 是 Java 中用于高精度计算的类,特别适用于财务计算和需要精确小数运算的场景。它克服了 float 和 double 类型可能出现的精度问题。
二、初始化
1. 常用初始化方式
// 1. 使用字符串初始化(推荐)
BigDecimal bd1 = new BigDecimal("10.50");
BigDecimal bd2 = new BigDecimal("3.25");
// 2. 使用 valueOf 方法
BigDecimal bd3 = BigDecimal.valueOf(10.50); // 内部使用字符串转换
BigDecimal bd4 = BigDecimal.valueOf(100); // 整数
// 3. 使用整数或双精度
BigDecimal bd5 = new BigDecimal(100); // 整数
BigDecimal bd6 = new BigDecimal(10.5); // 可能丢失精度,不推荐2. 常用常量
BigDecimal zero = BigDecimal.ZERO; // 0
BigDecimal one = BigDecimal.ONE; // 1
BigDecimal ten = BigDecimal.TEN; // 10三、基本算术运算
1. 加法
BigDecimal a = new BigDecimal("10.5");
BigDecimal b = new BigDecimal("3.2");
// 方法1: add()
BigDecimal sum = a.add(b); // 13.7
// 方法2: 带 MathContext(指定精度和舍入模式)
MathContext mc = new MathContext(4, RoundingMode.HALF_UP);
BigDecimal sum2 = a.add(b, mc); // 根据精度进行舍入2. 减法
BigDecimal a = new BigDecimal("10.5");
BigDecimal b = new BigDecimal("3.2");
// 方法1: subtract()
BigDecimal difference = a.subtract(b); // 7.3
// 方法2: 带 MathContext
MathContext mc = new MathContext(3, RoundingMode.HALF_UP);
BigDecimal difference2 = a.subtract(b, mc);3. 乘法
BigDecimal a = new BigDecimal("10.5");
BigDecimal b = new BigDecimal("3.2");
// 方法1: multiply()
BigDecimal product = a.multiply(b); // 33.60
// 方法2: 带 MathContext
MathContext mc = new MathContext(3, RoundingMode.HALF_UP);
BigDecimal product2 = a.multiply(b, mc); // 33.6
// 方法3: 指定精度和舍入模式
BigDecimal product3 = a.multiply(b, 2, RoundingMode.HALF_UP); // 33.604. 除法
BigDecimal a = new BigDecimal("10.5");
BigDecimal b = new BigDecimal("3.2");
// 方法1: 指定精度和舍入模式(必须)
BigDecimal quotient = a.divide(b, 2, RoundingMode.HALF_UP); // 3.28
// 方法2: 带 MathContext
MathContext mc = new MathContext(3, RoundingMode.HALF_UP);
BigDecimal quotient2 = a.divide(b, mc); // 3.28
// 方法3: 精确除法(如果不能整除会抛出 ArithmeticException)
try {
BigDecimal exactQuotient = a.divide(b);
} catch (ArithmeticException e) {
System.out.println("无法精确整除");
}
// 方法4: 除不尽时保留指定位数
BigDecimal quotient4 = a.divide(b, 10, RoundingMode.HALF_UP); // 保留10位小数四、舍入模式(RoundingMode)
Java 提供了多种舍入模式:
BigDecimal number = new BigDecimal("10.555");
// 1. 四舍五入(银行家舍入法)
BigDecimal rounded1 = number.setScale(2, RoundingMode.HALF_UP); // 10.56
BigDecimal rounded2 = number.setScale(2, RoundingMode.HALF_DOWN); // 10.55
BigDecimal rounded3 = number.setScale(2, RoundingMode.HALF_EVEN); // 10.56(最接近的偶数)
// 2. 向上取整
BigDecimal rounded4 = number.setScale(2, RoundingMode.CEILING); // 10.56
BigDecimal rounded5 = number.setScale(2, RoundingMode.UP); // 10.56
// 3. 向下取整
BigDecimal rounded6 = number.setScale(2, RoundingMode.FLOOR); // 10.55
BigDecimal rounded7 = number.setScale(2, RoundingMode.DOWN); // 10.55
// 4. 向零方向舍入
BigDecimal rounded8 = new BigDecimal("-10.555")
.setScale(2, RoundingMode.DOWN); // -10.55五、其他常用操作
1. 比较操作
BigDecimal a = new BigDecimal("10.5");
BigDecimal b = new BigDecimal("10.50");
// 使用 compareTo() 方法(推荐)
int result = a.compareTo(b); // 0 表示相等
// result < 0: a < b
// result == 0: a = b
// result > 0: a > b
// 判断相等(注意:10.5 和 10.50 使用 equals 不相等)
boolean equal1 = a.equals(b); // false(精度不同)
boolean equal2 = a.compareTo(b) == 0; // true(值相同)
// 最大值和最小值
BigDecimal max = a.max(b); // 返回较大值
BigDecimal min = a.min(b); // 返回较小值2. 取余/求模
BigDecimal a = new BigDecimal("10.5");
BigDecimal b = new BigDecimal("3.2");
// 方法1: remainder()
BigDecimal remainder = a.remainder(b); // 0.9
// 方法2: 带舍入模式
BigDecimal remainder2 = a.remainder(b, MathContext.DECIMAL32);3. 幂运算
BigDecimal base = new BigDecimal("2.5");
// 整数次幂
BigDecimal power = base.pow(3); // 2.5^3 = 15.625
// 带 MathContext 的幂运算
BigDecimal power2 = base.pow(3, MathContext.DECIMAL64);4. 绝对值
BigDecimal negative = new BigDecimal("-10.5");
BigDecimal absolute = negative.abs(); // 10.55. 取反
BigDecimal number = new BigDecimal("10.5");
BigDecimal negated = number.negate(); // -10.56. 精度和小数位数
BigDecimal number = new BigDecimal("123.456");
int precision = number.precision(); // 6(总位数)
int scale = number.scale(); // 3(小数位数)
BigInteger unscaledValue = number.unscaledValue(); // 123456
// 调整精度
BigDecimal scaled = number.setScale(2, RoundingMode.HALF_UP); // 123.46
BigDecimal scaled2 = number.setScale(4, RoundingMode.HALF_UP); // 123.45607. 移动小数点
BigDecimal number = new BigDecimal("123.456");
// 向左移动(乘以10的n次方)
BigDecimal moveLeft = number.movePointLeft(2); // 1.23456
// 向右移动(除以10的n次方)
BigDecimal moveRight = number.movePointRight(2); // 12345.6六、类型转换
BigDecimal bd = new BigDecimal("123.456");
// 转换为基本类型
int intValue = bd.intValue();
long longValue = bd.longValue();
float floatValue = bd.floatValue();
double doubleValue = bd.doubleValue();
// 精确转换(可能抛出异常)
int exactInt = bd.intValueExact(); // 如果不是整数则抛出异常
long exactLong = bd.longValueExact(); // 如果不是整数则抛出异常
BigInteger bigInteger = bd.toBigIntegerExact();
// 转换为字符串
String str = bd.toString(); // "123.456"
String plainStr = bd.toPlainString(); // "123.456"(无科学计数法)
String engStr = bd.toEngineeringString(); // 工程计数法表示七、工具类示例
import java.math.BigDecimal;
import java.math.RoundingMode;
public class BigDecimalUtils {
/**
* 安全加法(防止空指针)
*/
public static BigDecimal safeAdd(BigDecimal a, BigDecimal b) {
if (a == null && b == null) return BigDecimal.ZERO;
if (a == null) return b;
if (b == null) return a;
return a.add(b);
}
/**
* 四舍五入到指定位数
*/
public static BigDecimal round(BigDecimal value, int scale) {
if (value == null) return null;
return value.setScale(scale, RoundingMode.HALF_UP);
}
/**
* 判断是否为零
*/
public static boolean isZero(BigDecimal value) {
return value != null && value.compareTo(BigDecimal.ZERO) == 0;
}
/**
* 判断是否为正数
*/
public static boolean isPositive(BigDecimal value) {
return value != null && value.compareTo(BigDecimal.ZERO) > 0;
}
/**
* 判断是否为负数
*/
public static boolean isNegative(BigDecimal value) {
return value != null && value.compareTo(BigDecimal.ZERO) < 0;
}
/**
* 计算百分比
*/
public static BigDecimal percentage(BigDecimal part, BigDecimal total, int scale) {
if (total == null || isZero(total)) {
throw new ArithmeticException("除数不能为零");
}
return part.multiply(new BigDecimal("100"))
.divide(total, scale, RoundingMode.HALF_UP);
}
}八、最佳实践和注意事项
1. 初始化建议
- 优先使用字符串构造器:避免使用
double构造器,防止精度丢失 - 使用
valueOf方法:对于简单数值转换
2. 比较操作
- 使用
compareTo()而不是equals()进行值比较 equals()会同时比较值和精度(scale)
3. 除法注意事项
- 除法必须指定舍入模式,否则可能抛出
ArithmeticException - 考虑使用
MathContext控制整体精度
4. 财务计算
// 金额计算示例
public class MoneyCalculator {
// 货币计算通常保留2位小数
private static final int MONEY_SCALE = 2;
private static final RoundingMode MONEY_ROUNDING = RoundingMode.HALF_UP;
public static BigDecimal calculateTotal(List<BigDecimal> amounts) {
BigDecimal total = BigDecimal.ZERO;
for (BigDecimal amount : amounts) {
total = total.add(amount);
}
return total.setScale(MONEY_SCALE, MONEY_ROUNDING);
}
public static BigDecimal calculateTax(BigDecimal amount, BigDecimal taxRate) {
return amount.multiply(taxRate)
.setScale(MONEY_SCALE, MONEY_ROUNDING);
}
}5. 性能考虑
BigDecimal是 immutable(不可变)的,每次操作都会返回新对象- 在循环中频繁创建
BigDecimal可能影响性能,考虑重用对象
九、常见问题解决
1. 精度丢失问题
// 错误示例
BigDecimal bad = new BigDecimal(0.1); // 精度丢失
// 正确示例
BigDecimal good = new BigDecimal("0.1");
BigDecimal alsoGood = BigDecimal.valueOf(0.1);2. 除不尽异常
try {
BigDecimal result = a.divide(b); // 可能抛出 ArithmeticException
} catch (ArithmeticException e) {
// 处理除不尽的情况
BigDecimal result = a.divide(b, 10, RoundingMode.HALF_UP);
}3. 空指针问题
// 安全处理 null 值
BigDecimal safeValue = (value != null) ? value : BigDecimal.ZERO;十、总结
BigDecimal 提供了精确的数值计算能力,特别适合财务、科学计算等对精度要求高的场景。使用时需要注意:
- 正确的初始化方式
- 除法的舍入模式设置
- 使用
compareTo()进行数值比较 - 根据业务需求选择合适的精度和舍入模式
通过合理使用 BigDecimal,可以避免浮点数计算的精度问题,确保计算结果的准确性。
