金融系统中正确的金额计算及存储方式
时间:2022-05-06
本文章向大家介绍金融系统中正确的金额计算及存储方式,主要内容包括经典的精度丢失问题、怎么解决、总结、基本概念、基础应用、原理机制和需要注意的事项等,并结合实例形式分析了其使用技巧,希望通过本文能帮助到大家理解应用这部分内容。
昨天微信群里在讨论金额计算及存储的话题,今天特来结贴一下。
经典的精度丢失问题
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。当然,用整数存储分这种形式也可以,转账的时候单位为元而如果忘了转换分为元,那就悲剧了。
- 1820: [JSOI2010]Express Service 快递服务
- 3038: 上帝造题的七分钟2
- 1854: [Scoi2010]游戏
- Javascript字符串
- Codevs3278[NOIP2013]货车运输
- 关于使用lazytag的线段树两种查询方式的比较研究
- Java 持久化操作之 --XML
- 算法模板——splay区间反转 1
- 3223: Tyvj 1729 文艺平衡树
- 1212: [HNOI2004]L语言
- POJ 2942Knights of the Round Table(tarjan求点双+二分图染色)
- 算法模板——平衡树Treap
- Java并发编程
- 算法模板——线段树2(区间加+区间乘+区间求和)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Anaconda使用命令
- 最大连续子数组
- markdown编辑器实现代码高亮
- LeetCode - 198 简单动态规划 打家劫舍
- Jaskson精讲第7篇-JsonTypeInfo注解在类继承关系下的使用
- 《剑指Offer 1.二维数组中的查找》2019-03-25
- Elasticsearch:Dynamic mapping
- 设计模式《单例设计模式》
- 《0-1 背包问题》
- 使用分治思想 求数组中的最大和最小值
- python 入门笔记[语法基础(下)]
- Java 成员变量和属性的区别
- xmuC语言程序实践week 1 大作业
- 《01-背包问题-点菜》
- 模拟赛 2018 Benelux Algorithm Programming Contest (BAPC 18)(部分题)