金融系统中正确的金额计算及存储方式

昨天微信群里在讨论金额计算及存储的话题,今天特来结贴一下。

经典的精度丢失问题

Java中的类型float、double用来做计算会有精度丢失问题,下面来看下面的示例。

1
2
3
4
5
6
7
8
9
10
11
public static void main(String[] args) {
test1();
test2();
}

private static void test1() {
double totalAmount = 0.09;
double feeAmount = 0.02;
double tradeAmount = totalAmount - feeAmount;
System.out.println(tradeAmount);
}

上面的程序输出结果是多少?

怎么解决

在《Effective Java》这本书中也提到这个原则,float和double只能用来做科学计算或者是工程计算,在商业计算中我们要用 java.math.BigDecimal。

BigDecimal适合更精度的运算,也提供了丰富的操作符类型,小数位控制,四舍五入规则等。

不过,使用BigDecimal不当也有精度丢失的情况,如double的构造方法:

1
BigDecimal(double val)

再来看这个示例:

1
2
3
4
5
6
private static void test2() {
double totalAmount = 0.09;
double feeAmount = 0.02;
BigDecimal tradeAmount = new BigDecimal(totalAmount).subtract(new BigDecimal(feeAmount));
System.out.println(tradeAmount);
}

输出:

1
0.0699999999999999962529972918900966760702431201934814453125

这个精度就更恐怖了。。

所以,一定要使用String的构造方法:

1
BigDecimal(String val)
1
2
3
4
5
6
7
private static void test3() {
double totalAmount = 0.09;
double feeAmount = 0.02;
BigDecimal tradeAmount = new BigDecimal(String.valueOf(totalAmount))
.subtract(new BigDecimal(String.valueOf(feeAmount)));
System.out.println(tradeAmount);
}

总结

  1. 金额运算尽量使用BigDecimal(String val)进行运算。

  2. 数据库存储金额,一般有整型和浮点型两种存储方式。如果是有汇率转换的,建议使用浮点数decimal进行存储,可以灵活的控制精度,decimal直接对应java类型BigDecimal。当然,用整数存储分这种形式也可以,转账的时候单位为元而如果忘了转换分为元,那就悲剧了。


发布于 2020-04-01 21:08:43
分享
海报
180
上一篇:通用唯一标识码UUID的介绍及使用。下一篇:Java中的宏变量,宏替换详解。
目录

    忘记密码?

    图形验证码