前言
Java浮点数的比较是开发中经常遇到的问题,但是很容易出错。
本文主要介绍了浮点数比较的正确方法和常见的注意事项。
一、浮点数比较存在误差的原因
简单来说:浮点数比较的时候,精度会丢失,比较结果可能是不对的。
因为浮点数比较的时候会涉及到类型升级(type promotion),而且二进制无法存储一些数字(如3.1415926…)等等的准确值,因此在存储的时候会舍入部分值,也就造成了误差。
同时,float 和 double的精度也不一样,也会因此产生误差。
具体可以参考这篇文章:Java中的浮点数比较 == equals 和 compare
二、浮点数比较的正确方法
1.设置误差范围
原理分析:当两个浮点数之间的差值小于一定限制值时,我们就认为这两个数字相等。
注意:这个限制值,可以根据系统的具体要求和所处环境进行设定,一般是10的-7次方或者10的-14次方。
示例代码如下:
final double limitValue = .000001;
double d1 = .1 * 3;
double d2 = .3;
if(Math.abs(d1-d2) < limitValue) {
System.out.println("d1 和 d2 相等");
} else {
System.out.println("d1 和 d2 不相等");
}
2.利用BigDemical
不熟悉这个类的朋友可以先去了解一下有关知识:java 中 BigDecimal 详解
注意:BigDemical构造数字时,一定要传入字符串而不是数字:
BigDecimal num=new BigDecimal(String) 创建一个具有参数所指定以字符串表示的数值的对象。//推荐使用
原因在于:传入的数字是小数时,本身是个不精确的值,而BigDecimal存储的精确运算,用精确的类型存储不精确的数字就很容易出现问题。
BigDecimal num=new BigDecimal(0.1d) //不推荐使用
示例代码如下:
BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("0.9");
BigDecimal c = new BigDecimal("0.8");
BigDecimal x = a.subtract(b);
BigDecimal y = b.subtract(c);
if (x.equals(y)) {
System.out.println("true");
}
更多内容可以参考:Java中的浮点数到底应该怎么比较,才算是最优解?你真的知其然,知其所以然了吗?
三、拓展:Java的0.0和-0.0陷阱
不熟悉的朋友看到这个标题可能会很吃惊:0.0和-0.0不是一样的吗?为什么说是一个陷阱?
原因在于:在Java中,double 区分+ 0.0 和- 0.0 ,也就是说 +0.0 和 -0.0 是两个不同的数字,且+0.0 大于 -0.0。
因为具体存储的时候有一位是符号位,导致+0.0 和 -0.0在内部存储表示的时候并不相同,但是值相同
可以参考:
java浮点数,-0.0小于+0.0
为什么我们在Ruby中有0.0和-0.0?
Java浮点型比较的正确方法
代码示例:
System.out.println(0.0 == -0.0);
System.out.println(Double.compare(0.0, -0.0));
System.out.println(new Double(0.0).equals(new Double(-0.0)));
Map<Double, Integer> map = new HashMap<>();
map.put(0.0, 1);
map.put(-0.0, 2);
System.out.println(map.size());
System.out.println((double)(2-2)/(1-2));
System.out.println((double)(3-3)/(2-1));
输出结果:
true
1
false
2
-0.0
0.0
因此,用HashSet存储的时候,如果key为浮点数,那么一定要注意,0.0和-0.0可以当做两个Key往HashSet插值。
解决办法为:set.add(num+0.0) 这样不会影响具体值,还能克服0.0和-0.0问题。
总结
本文主要介绍了Java浮点数比较存在误差的原因,介绍了浮点数比较的正确方法。
易错点在于:
(1)BigDemical构造数字时,一定要传入字符串而不是数字
(2)0.0和-0.0问题
希望对大家有用!