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

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

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

经典的精度丢失问题

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

总结

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

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

推荐阅读


去BAT面试完的Mysql面试题总结(55道,带完整答案)

阿里高级Java面试题(首发,70道,带详细答案)

2017派卧底去阿里、京东、美团、滴滴带回来的面试题及答案

Spring面试题(70道,史上最全)

通往大神之路,百度Java面试题前200页。

分享Java干货,高并发编程,热门技术教程,微服务及分布式技术,架构设计,区块链技术,人工智能,大数据,Java面试题,以及前沿热门资讯等。


相关文章
|
机器学习/深度学习 人工智能 自然语言处理
32B小模型竟能吊打百亿参数?GLM-4-Air-0414:智谱AutoGLM沉思背后的模型,智能体开发迎来新纪元
GLM-4-Air-0414是智谱公司推出的320亿参数开源基座模型,通过优化预训练数据和对齐策略,在工具调用、联网搜索和代码生成等智能体任务中展现出卓越性能。
636 15
32B小模型竟能吊打百亿参数?GLM-4-Air-0414:智谱AutoGLM沉思背后的模型,智能体开发迎来新纪元
|
12月前
|
传感器 SQL 运维
2025 年中国中小企业数字化转型:Websoft9 开源托管平台的价值
Websoft9 以开源技术为核心,打造零门槛、低成本的数字化基座,提供 200+ 开源模板(如 Odoo、Nextcloud),助力企业快速部署与扩展。通过容器化技术、多云适配及主动防御体系,保障安全与兼容性。行业级解决方案覆盖制造、教育、法律等领域,实现数据驱动决策闭环。生态创新模式鼓励技术反哺与商业裂变,形成“标准化模板 + 自由扩展”路径,使中小企业从技术消费者转型为生态共建者,推动数字化转型成为价值创造的永动机。
|
存储 Java
|
监控 Java 数据库连接
线程池在高并发下如何防止内存泄漏?
线程池在高并发下如何防止内存泄漏?
515 6
|
Java 编译器 Spring
面试突击78:@Autowired 和 @Resource 有什么区别?
面试突击78:@Autowired 和 @Resource 有什么区别?
17436 7
|
存储 缓存 安全
|
算法 Java 开发者
Java中的垃圾回收机制:从原理到实践
Java的垃圾回收机制(Garbage Collection, GC)是其语言设计中的一大亮点,它为开发者提供了自动内存管理的功能,大大减少了内存泄漏和指针错误等问题。本文将深入探讨Java GC的工作原理、不同垃圾收集器的种类及它们各自的优缺点,并结合实际案例展示如何调优Java应用的垃圾回收性能,旨在帮助读者更好地理解和有效利用Java的这一特性。
|
人工智能 前端开发 搜索推荐
移动应用开发的未来趋势:跨平台框架与AI的融合
【7月更文挑战第4天】随着移动设备的普及和用户需求的多样化,移动应用开发正面临前所未有的挑战与机遇。本文将深入探讨跨平台框架在提高开发效率、降低成本方面的优势,以及人工智能技术如何赋能移动应用,提供更加智能化的用户体验。我们将分析当前市场上流行的跨平台工具如Flutter和React Native,并探讨AI技术在移动应用中的具体应用案例,预测未来移动应用开发的发展方向。
340 2
|
人工智能 自然语言处理 开发者
Langchain 与 Elasticsearch:创新数据检索的融合实战
Langchain 与 Elasticsearch:创新数据检索的融合实战
|
机器学习/深度学习 编解码 算法
万字长文解读图像超分辨率 Real-ESRGAN 论文笔记+代码阅读
万字长文解读图像超分辨率 Real-ESRGAN 论文笔记+代码阅读
2922 3