切换语言为:繁体

金额计算工具类 BigDecimal 详解及容易出现的问题汇总

  • 爱糖宝
  • 2024-11-14
  • 2025
  • 0
  • 0

前言

在电商系统,金额计算的精确性和安全性至关重要。由于浮点数的固有特性,使用传统的floatdouble类型进行货币计算可能会导致精度丢失,从而引发一系列问题。为了解决这一问题,Java中的BigDecimal类应运而生,成为了处理金额计算的首选工具,并且在面试中,经常也会被问到下面这些问题,一定涉及过金额处理的操作吧?请问你用那个类型?精度如何处理的?有没有碰到过坑?

为什么选择BigDecimal?

BigDecimal类提供了任意精度的带符号十进制数,非常适合需要精确小数点运算的场景。与floatdouble不同,BigDecimal对象是不可变的,并且其精度和舍入模式都是可控的,这使得它在金融计算中具有显著优势。 可以看这个例子,就知道为什么不用double。

private static void doubleDemo() {
    double amount1 = 0.03;
    double amount2 = 0.02;
    //执行结果会是多少?
    // 0.009999999999999998
    System.out.println(amount1 - amount2);

}

输出结果是:

金额计算工具类 BigDecimal 详解及容易出现的问题汇总

精度控制

在使用BigDecimal时,可以通过构造函数或静态工厂方法valueOf来创建对象。值得注意的是,直接使用double类型的值来构造BigDecimal可能会导致精度损失。因此,推荐使用String类型的参数来创建BigDecimal实例,以确保数值的精确表示。

BigDecimal amount1 = new BigDecimal(0.03);
BigDecimal amount2 = new BigDecimal(0.02);
//12.【强制】禁止使用构造方法BigDecimal(double) 的方式把double值转化为BigDecimal对象,非要转那?
System.out.println("amount1: " + amount1);
System.out.println("amount2: " + amount2);
//应该等于0.01
System.out.println(amount1.subtract(amount2));
System.out.println();

运行结果如下:

金额计算工具类 BigDecimal 详解及容易出现的问题汇总

等值比较

在进行金额比较时,应避免使用equals方法,因为它不仅比较数值,还比较精度。相反,应该使用compareTo方法,该方法仅比较数值大小,更适合金融计算的需求。

private static void m2() {
    BigDecimal amount1 = new BigDecimal("0.9");
    BigDecimal amount2 = new BigDecimal("0.90");
   
    System.out.println("equals比较结果:" + amount1.equals(amount2));
    System.out.println("compareTo比较结构:" + amount1.compareTo(amount2));
}

运行结果:

金额计算工具类 BigDecimal 详解及容易出现的问题汇总

除法运算

BigDecimal的除法运算需要特别注意,因为它可能产生无限循环小数。为了避免这种情况,需要指定结果的精度和舍入模式。 BigDecimal 的 8 种 RoundingMode(舍入模式),下面两种为常见模式:

  • ROUND_HALF_UP 向 “最接近” 的数字舍入,如果与两个相邻数字的距离相等,则为向上舍入的舍入模式。 如果舍弃部分 >= 0.5,则舍入行为与 ROUND_UP 相同;否则舍入行为与 ROUND_DOWN 相同。 这种模式也就是我们常说的我们的 “四舍五入”。

  • ROUND_HALF_DOWN 向 “最接近” 的数字舍入,如果与两个相邻数字的距离相等,则为向下舍入的舍入模式。 如果舍弃部分 > 0.5,则舍入行为与 ROUND_UP 相同;否则舍入行为与 ROUND_DOWN 相同。 这种模式也就是我们常说的我们的 “五舍六入”。 案例代码如下:

private static void m3() {
    BigDecimal amount1 = new BigDecimal("2.0");
    BigDecimal amount2 = new BigDecimal("3.0");
    //System.out.println(amount1.divide(amount2)); //Non-terminating decimal expansion; no exact representable decimal result.
    //我们该如何处理?
    System.out.println(amount1.divide(amount2, 2, RoundingMode.HALF_UP)); //这种模式也就是我们常说的我们的 “四舍五入”。
}

运行结果:

金额计算工具类 BigDecimal 详解及容易出现的问题汇总

数据库设计建议

在设计数据库表时,金额字段应使用DECIMAL类型而非INT类型。DECIMAL类型可以指定小数点后的位数,确保数值的精确存储。例如,DECIMAL(10, 2)可以存储最多两位小数的数值,总长度为10位。

结论

在处理金额计算时,BigDecimal类提供了必要的精确性和安全性。通过合理使用BigDecimal,可以避免由于浮点数精度问题导致的计算错误,确保金融系统的可靠性和稳定性。无论是电商平台的支付模块,还是金融应用中的交易系统,正确使用BigDecimal都是保障业务顺利进行的关键


作者:小明爱吃火锅
链接:https://juejin.cn/post/7437054131557564456

0条评论

您的电子邮件等信息不会被公开,以下所有项均必填

OK! You can skip this field.