经典的精度丢失问题
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。当然,用整数存储分这种形式也可以,转账的时候单位为元而如果忘了转换分为元,那就悲剧了。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 字符编码:从基础到乱码解决