大家都知道,不能用Float和Double来表示金额,会存在丢失精度的问题。
那么要表示金额,业内有两种做法:
1、单位为分,数据库存bigint,代码中用long
2、单位为元,数据库用decimal,代码中用BigDecimal(我们一般数据库存储的是decimal(18,6))
这两种,其实我们都用过,而且现在也还都在用,因为他们都有各自的优缺点以及适用场景。
首先说BigDecimal,BigDecimal
是 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>
可能更适合。