Skip to content
On this page

Java 中金额的计算

推荐使用 BigDecimal

Java 的 Math 类(比如 Math.round())虽然方便,但解决不了精度问题,它只是帮你做取整,而不是解决浮点数本身精度丢失的问题。

为什么 BigDecimal 是解法?

因为 BigDecimal 使用十进制表示数字,而不是二进制,所以能精确表示 0.1、0.2 这样的小数,而不会像 float/double 那样变成 0.10000000000000000555...

通用工具类推荐

java
import java.math.BigDecimal;
import java.math.RoundingMode;

/**
 * 精确计算工具类 - 解决Java浮点数精度丢失问题
 */
public class BigDecimalUtil {

    // 默认精度(小数点后2位,常用在金融计算)
    private static final int DEFAULT_SCALE = 2;
    // 默认舍入模式(四舍五入)
    private static final RoundingMode DEFAULT_ROUNDING = RoundingMode.HALF_UP;

    // 私有构造方法,防止实例化
    private BigDecimalUtil() {}

    /**
     * 精确加法
     * @param v1 被加数
     * @param v2 加数
     * @return 两个数的和
     */
    public static BigDecimal add(double v1, double v2) {
        return add(String.valueOf(v1), String.valueOf(v2));
    }

    /**
     * 精确加法(推荐使用字符串形式,避免精度丢失)
     * @param v1 被加数(字符串形式)
     * @param v2 加数(字符串形式)
     * @return 两个数的和
     */
    public static BigDecimal add(String v1, String v2) {
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.add(b2);
    }

    /**
     * 精确减法
     */
    public static BigDecimal subtract(double v1, double v2) {
        return subtract(String.valueOf(v1), String.valueOf(v2));
    }

    public static BigDecimal subtract(String v1, String v2) {
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.subtract(b2);
    }

    /**
     * 精确乘法
     */
    public static BigDecimal multiply(double v1, double v2) {
        return multiply(String.valueOf(v1), String.valueOf(v2));
    }

    public static BigDecimal multiply(String v1, String v2) {
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.multiply(b2);
    }

    /**
     * 精确除法(推荐指定精度和舍入模式)
     */
    public static BigDecimal divide(double v1, double v2, int scale) {
        return divide(String.valueOf(v1), String.valueOf(v2), scale, DEFAULT_ROUNDING);
    }

    public static BigDecimal divide(double v1, double v2) {
        return divide(String.valueOf(v1), String.valueOf(v2), DEFAULT_SCALE, DEFAULT_ROUNDING);
    }

    public static BigDecimal divide(String v1, String v2, int scale, RoundingMode roundingMode) {
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.divide(b2, scale, roundingMode);
    }

    /**
     * 格式化为字符串(带精度)
     */
    public static String format(BigDecimal value, int scale) {
        return value.setScale(scale, DEFAULT_ROUNDING).toString();
    }
}

  • 使用字符串构造:避免了 new BigDecimal(0.1)这种错误用法,直接用 new BigDecimal("0.1"),确保精度
  • 默认精度设置:金融计算通常需要保留 2 位小数,所以默认是 2 位
  • 舍入模式:默认使用 HALF_UP(四舍五入),符合大多数业务需求
  • 避免了 double 的精度问题:所有方法都推荐使用字符串形式

使用示例

java
// 正确做法:使用字符串形式
BigDecimal result = BigDecimalUtil.add("0.1", "0.2");
System.out.println(result); // 输出: 0.3

// 错误做法:使用double,会导致精度问题
BigDecimal wrongResult = new BigDecimal(0.1 + 0.2);
System.out.println(wrongResult); // 输出: 0.30000000000000004

注意事项

  • 不要用 double 构造 BigDecimal:new BigDecimal(0.1)会出问题,必须用 new BigDecimal("0.1")
  • 除法必须指定精度:divide()方法必须指定精度,否则可能抛出 ArithmeticException
  • 性能考虑:BigDecimal 比基本类型慢,但对精度要求高的场景(如金融)是值得的

上次更新于: