✅BigDecimal和Long表示金额哪个更合适,怎么选择?

典型回答

大家都知道,不能用Float和Double来表示金额,会存在丢失精度的问题。

✅为什么不能用浮点数表示金额?

那么要表示金额,业内有两种做法:

1、单位为分,数据库存bigint,代码中用long

2、单位为元,数据库用decimal,代码中用BigDecimal(我们一般数据库存储的是decimal(18,6)

这两种,其实我们都用过,而且现在也还都在用,因为他们都有各自的优缺点以及适用场景。

首先说BigDecimalBigDecimal 是 Java 中用于精确计算的类,特别适合于需要高精度数值计算的场景,如金融、计量和工程等领域。其特点如下:

  • 精确度高BigDecimal 可以表示非常大或非常精确的小数,而不会出现浮点数那样的舍入误差。
  • 灵活的数学运算:它提供各种方法进行精确的算术操作,包括加减乘除和四舍五入等。
  • 控制舍入行为:在进行数学运算时,你可以指定舍入模式,这对于金融计算非常重要。

所以,BigDecimal的适用场景是需要高精度计算的金融应用,如货币计算、利率计算等。比如我们的结算系统、支付系统、账单系统等,都是用BigDecimal的。

其次,再说Long,long 是 Java 的一种基本数据类型,用于表示没有小数部分的整数。其特点如下:

  • 性能高:作为基本数据类型,long 在处理速度上比 BigDecimal 快很多。
  • 容量限制long 可以表示的最大值为 (2^{63}-1),最小值为 (-2^{63})。这在大多数应用程序中已经足够,但在表示非常大的数或需要小数的计算中则不适用。
  • 不适合精确的小数运算long 无法处理小数,如果需要代表金额中的小数部分(如厘),则需要自行管理这一部分。

所以,Long的适用场景是适合于不涉及小数计算的大整数运算,如某些计数应用或者金额以整数形式表示。比如我们的额度系统、积分系统等。


很多人会有疑惑,什么情况下会出现需要比分还小的单位呢?其实就是在很多需要运算的场景,比如说金融的费率、利率、服务费的费率等等,这些都是很小的,一般都是万分之几或者千分之几。而一旦有一个单位为元的金额和一个"率"相乘的时候,就会出现小于分的单位。

那有人说,遇到分我就直接四舍五入不就行了么,反正结算也是按照分结算的。这样做会有问题,我举个例子。

我一笔账单,有两笔订单,金额都是1元,存储的时候按照分存储,即100分,然后我的服务费费率是0.004。

如果是以分为单位,long存储和表示的话,那么两笔订单分开算费率的话:100*0.004 = 0.4 ,四舍五入 0, 两笔加在一起,收入的费率就是0分。

但是如说是以元为单位,bigdecimal存储和表示的话,那么两笔订单分开算费率的话:1*0.004 = 0.004 , 两笔加在一起0.008,当我要结算的时候,再做四舍五入就是0.01元,即1分钱。

所以,因为long在计算和存储的过程中都会丢失掉小数部分,那就会导致每一次都被迫需要四舍五入。而decimal完全可以保留过程中的数据,再最终需要的时候做一次整体的四舍五入,这样结果就会更加精确!

所以,如果你的应用需要处理小数点后的精确计算(如金融计算中常见的多位小数),则应选择 BigDecimal

如果你的应用对性能要求极高,并且没有乘除类运算,不需要很小的精度时,那么使用 long 可能更合适。

总结来说,对于绝大多数涉及货币计算的应用,推荐使用 <font style="background-color:#FBDE28;">BigDecimal</font>,因为它提供了必要的精度和灵活性,尽管牺牲了一些性能。如果确定不需要处理小数,并且对执行速度有极端要求,使用 <font style="background-color:#FBDE28;">long</font> 可能更适合。

原文: https://www.yuque.com/hollis666/xkm7k3/skv2srz4h3786nng