Skip to content

BigDecimal操作手册

约 1735 字大约 6 分钟

手册

2025-12-26

BigDecimal 是 Java 中用于高精度计算的类,特别适用于财务计算和需要精确小数运算的场景。它克服了 floatdouble 类型可能出现的精度问题。

Java BigDecimal 操作手册

一、概述

BigDecimal 是 Java 中用于高精度计算的类,特别适用于财务计算和需要精确小数运算的场景。它克服了 floatdouble 类型可能出现的精度问题。

二、初始化

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.60

4. 除法

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.5

5. 取反

BigDecimal number = new BigDecimal("10.5");
BigDecimal negated = number.negate();  // -10.5

6. 精度和小数位数

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.4560

7. 移动小数点

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 提供了精确的数值计算能力,特别适合财务、科学计算等对精度要求高的场景。使用时需要注意:

  1. 正确的初始化方式
  2. 除法的舍入模式设置
  3. 使用 compareTo() 进行数值比较
  4. 根据业务需求选择合适的精度和舍入模式

通过合理使用 BigDecimal,可以避免浮点数计算的精度问题,确保计算结果的准确性。