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

简介: 昨天微信群里在讨论金额计算及存储的话题,今天特来结贴一下。经典的精度丢失问题Java中的类型float、double用来做计算会有精度丢失问题,下面来看下面的示例。

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

经典的精度丢失问题

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

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);
}

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


0.07?非也!


正确的结果是:

0.06999999999999999


为什么是这样?


浮点数可能丢失精度,浮点十进制数通常没有完全相同的二进制的表示形式,这是CPU所采用的浮点数据表示形式的副作用。为此,可能会有一些精度丢失,并且一些浮点运算可能会产生未知的结果。


浮点运算很少是精确的,只要是超过精度能表示的范围就会产生误差。所以,在使用float、double作精确运算的时候一定要特别小心,除非能容忍精度丢失,不然产生的误差也是会造成双方对账不一致的结果。


怎么解决

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


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


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

BigDecimal(double val)

再来看这个示例:

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);
}

输出:

0.0699999999999999962529972918900966760702431201934814453125

这个精度就更恐怖了。。

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

BigDecimal(String val)
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);
}

总结

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


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


相关文章
|
SQL 关系型数据库 SDN
双活中心数据一致性
双活中心数据一致性
868 2
|
编译器 C语言 C++
右值引用,完美转发,NRVO 和RVO优化(简单易懂详细)
右值引用,完美转发,NRVO 和RVO优化(简单易懂详细)
1609 0
|
人工智能 网络协议 算法
5 分钟搞懂 ECN
5 分钟搞懂 ECN
3787 0
|
8月前
|
数据采集 人工智能 自然语言处理
让AI读懂代码需求:模块化大模型微调助力高效代码理解与迁移
本文介绍了一种解决开源项目代码升级中“用户需求关联相应代码”难题的创新方法。面对传统Code RAG和Code Agent在召回率、准确率和稳定性上的不足,以及领域“黑话”和代码风格差异带来的挑战,作者团队提出并实践了一套以大模型微调(SFT)为核心的解决方案。
1200 21
|
算法
递归和迭代详解
递归和迭代详解
1278 1
|
机器学习/深度学习 数据可视化 算法框架/工具
Python小项目:利用U-net完成细胞图像分割
这个项目能够锻炼你的深度学习技能,同时也能在医学、生物等领域有实际应用。你可以参考相关的教程和资源,如 GitHub 上的 U-Net 项目,以获得更详细的指导。 买CN2云服务器,免备案服务器,高防服务器,就选蓝易云。百度搜索:蓝易云
307 3
|
存储 安全 算法
【C++ 包装器类 std::atomic 】全面入门指南:深入理解并掌握C++ std::atomic 原子操作 的实用技巧与应用
【C++ 包装器类 std::atomic 】全面入门指南:深入理解并掌握C++ std::atomic 原子操作 的实用技巧与应用
1806 1
|
存储 安全 C++
【C++ 泛型编程 高级篇】C++可变参数模板探索:编程技巧与实战应用
【C++ 泛型编程 高级篇】C++可变参数模板探索:编程技巧与实战应用
910 0
|
Prometheus Cloud Native 数据安全/隐私保护
Prometheus实战篇:docker安装Prometheus
Docker搭建Prometheus监控系统