如果我使用这样的比较(a是int,b和c是float/double),那么是否安全:
a == b
b == c
也许听起来很荒谬,但在我以前的编程语言中,有时候 1 + 2 == 3 是错误的(因为左侧返回的是 2.99999999999...)。那么这个呢:
Math.sqrt(b) == Math.sqrt(c)
b / 3 == 10 / 3 //In case b = 10, does it return true?
如果我使用这样的比较(a是int,b和c是float/double),那么是否安全:
a == b
b == c
也许听起来很荒谬,但在我以前的编程语言中,有时候 1 + 2 == 3 是错误的(因为左侧返回的是 2.99999999999...)。那么这个呢:
Math.sqrt(b) == Math.sqrt(c)
b / 3 == 10 / 3 //In case b = 10, does it return true?
一般来说,由于许多十进制数不能被准确地表示为 float
或 double
值,因此通常情况下不安全。经常提到的解决方案是测试这些数字之间的差异是否小于某个“小”值(在数学文献中通常用希腊字母“epsilon”符号表示)。
但是,你需要注意如何进行测试。例如,如果你写成:
if (Math.abs(a - b) < 0.000001) {
System.err.println("equal");
}
如果在测试中,a
和b
应该是“相同的”,则应该使用绝对误差。但这样做可能会遇到麻烦,例如当a
和b
分别为1,999,999.99
和2,000,000.00
等值时,这两个数字之间的差异比一个 float
数据类型表示的最小值(在那个级别上)还要小,但它仍然远大于我们选择的 epsilon。
可以说,更好的方法是使用相对误差;例如,以以下方式进行(防御性地编码)
if (a == b ||
Math.abs(a - b) / Math.max(Math.abs(a), Math.abs(b)) < 0.000001) {
System.err.println("close enough to be equal");
}
但是,即使如此仍然不是完整的答案,因为它没有考虑到某些计算方式会导致误差累积到难以控制的程度。请查看这个维基百科链接了解更多详细信息。
底线是,在浮点数计算中处理错误比表面上看起来要困难得多。
还有一点需要注意(就像其他人已经解释过的那样),整数算术在某些方面与浮点算术表现非常不同:
这两种情况都会在没有任何警告的情况下发生,无论是在编译时还是运行时。
您需要注意一些细节。
1.0 + 2.0 == 3.0
这是正确的,因为整数可以被精确地表示。
Math.sqrt(b) == Math.sqrt(c)
b / 3.0 == 10.0 / 3.0
如果b == 10.0,这是我认为你的意思。
最后两个示例比较了同一计算的两个不同实例。当您具有不可表示数字的不同计算时,精确相等测试会失败。
如果您正在测试浮点逼近的计算结果,则应该进行容差的相等性测试。
您有任何具体的现实世界的例子吗?我认为您会发现希望使用浮点数进行相等测试是很少的。
10.0/5.0
的结果为2.0
,并且可以精确表示。此外,在这样的环境中,1.0+2.0==3.0
。了解你能做什么以及不能做什么,以及边界在哪里非常重要。 - David Heffernan2
是由10
除以一个number
(来自另一位队友编写的函数)得出的结果,在理论/算法上,它应该是整数(尽管返回类型是double)。所以问题可能来自于那个函数。 - Luke Vo在比较浮点数/双精度浮点数时,最安全的方法实际上是检查它们之间的差是否为一个小数。
例如:
Math.abs(a - b) < EPS
EPS可以是0.0000001之类的数字。
这样做可以确保精度误差不会影响您的结果。
b / 3 != 10 / 3
- 如果b是一个浮点变量,如b = 10.0f
,那么b / 3是3.3333,而10 / 3
是整数除法,所以等于3。
如果b == c
,那么Math.sqrt(b) == Math.sqrt(c)
- 这是因为sqrt函数总是返回double类型。
通常来说,你不应该直接比较双精度/浮点数的相等性,因为它们是浮点数,可能会出现误差。你几乎总是希望使用给定的精度进行比较,例如:
b - c < 0.000001
在基本上任何语言中,对于双精度浮点数来说,“==”比较并不是特别安全的。最好采用一个epsilon比较方法(即检查两个浮点数之间的差距是否足够小)。
例如:
Math.sqrt(b) == Math.sqrt(c)
b / 3 == 10 / 3
因为整数除法的原因,10/3=3,所以这不一定会给出你想要的结果。你可以使用10.0/3,但我仍然不确定为什么你不直接比较b和10(在任何情况下都使用epsilon比较方法)。
Float.compare(float1,float2)==0
float1 == float2
完全做同样的事情! - Stephen Cb = flaot (meance ans 3.33333333333333333333333)
10 is integer so that make sence.
2位小数的高效方法:
public static void main(String[] args) {
double number = 3.14999965359;
float number2 = 3.14909265359f;
Date date = new Date();
System.out.println(equalsDoubleWithFloat(number, number2));
System.out.println(isDoubleBigFromFloat(number, number2));
System.out.println("delay: " + (new Date().getTime() - date.getTime()) + " ms");
}
private static boolean equalsDoubleWithFloat(double db, float fl) {
return Float.compare((float) db, fl) == 0 || Math.abs(db - fl) < 0.01;
}
private static boolean isDoubleBigFromFloat(double db, float fl) {
return Math.abs(db - fl) > 0.01 && Float.compare((float) db, fl) > 0;
}
public static boolean isDoubleSmallFromFloat(double db, float fl) {
return Math.abs(db - fl) > 0.01 && Float.compare((float) db, fl) < 0;
}
输出结果为:
true
false
delay: 1 ms
其他解决方案:
private static final DecimalFormat TWO_DIGITS = new DecimalFormat("0.00");
public static double formatDouble(double value) {
return Double.parseDouble(TWO_DIGITS.format(value));
}
public static float formatFloat(float value) {
return Float.parseFloat(TWO_DIGITS.format(value));
}
public static int compareDoubleWithFloat(double db, float fl) {
double roundedDouble = formatDouble(db);
float roundedFloat = formatFloat(fl);
if (Math.abs(roundedDouble - roundedFloat) < 0.01) {
return 0;
} else if (roundedDouble > roundedFloat) {
return 1;
} else {
return -1;
}
}