Java double和float精度损失问题

问题

一般,我们会用float和double来存储有小数的数据,可能还会用两个浮点型数据进行计算,在某些情况下,float和double会出现精度损失,导致存储的数据或者计算结果出现误差。

举个例子

public class Test {

	public static void main(String[] args) {
		double a = 0.11;
		double b = 2.32;
		System.out.println(a + b);
		System.out.println(0.11 + 2.32);
		
		float c = 0.11f;
		float d = 2.32f;
		System.out.println(c + d);
	}
}

执行结果如下:

2.4299999999999997
2.4299999999999997
2.4299998

这个结果并不是我们预期的2.43。很明显,精度损失了,虽然仅比原来小了0.00000000000003,但是如果是我们银行卡里的金额,那可就出大问题了。
经过测试,还发现以下两种情况:

  1. 如果把上面的2.32换成2.02,double类型计算得到正确结果,而float类型依然计算错误:
2.13
2.13
2.1299999
  1. 如果把2.32换成1.02,居然反过来了,float类型计算正确,double类型计算错误:
1.1300000000000001
1.1300000000000001
1.13

气人啊!

原因

计算机用0和1来表示数据,并没有小数,浮点型数据采用IEEE754标准,浮点型数据如何存储可以参考这篇文章
《Effective Java》中写道:

float 和 double 类型主要用于科学计算和工程计算。它们执行二进制浮点运算,该算法经过精心设
计,能够在很大范围内快速提供精确的近似值。但是,它们不能提供准确的结果,也不应该在需要精确
结果的地方使用。float 和 double 类型特别不适合进行货币计算,因为不可能将 0.1(或 10 的任意负次
幂)精确地表示为 float 或 double。

所有的编程语言中应该都有这个问题,而不仅仅是Java。

解决方法

那么,如何解决这个问题呢?
用java.math.BigDecimal,并且根据《阿里Java开发规范》,应该使用BigDecimal(“0.01”)这种方式创建对象,而不能用BigDecimal(0.01)这种方式。

12.【强制】禁止使用构造方法 BigDecimal(double)的方式把 double 值转化为 BigDecimal 对象。
说明:BigDecimal(double)存在精度损失风险,在精确计算或值比较的场景中可能会导致业务逻辑异常。
如:BigDecimal g = new BigDecimal(0.1F); 实际的存储值为:0.10000000149
正例:优先推荐入参为 String 的构造方法,或使用 BigDecimal 的 valueOf 方法,此方法内部其实执行了
Double 的 toString,而 Double 的 toString 按 double 的实际能表达的精度对尾数进行了截断。
BigDecimal recommend1 = new BigDecimal(“0.1”);
BigDecimal recommend2 = BigDecimal.valueOf(0.1)

另外,在数据库中,也应该使用decimal,而不是float和double。

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值