检查双精度浮点数是否相等是安全的吗?

28

我有以下的代码:

double x = 0;

{ ...do stuff ...}

if(x == 0){

}

我一直被教导不应该检查浮点数是否相等。但是检查它是否等于零有所不同吗?


我不明白,如果你将x初始化为0,为什么检查是否为0不好?0是double的有效值。 - JohnJohnGa
1
浮点数在接近极限时会有很多舍入。同样的原因是,如果你有3个十进制数字要使用,你有.004并且除以三,你期望得到.001,但谁知道会发生什么。 - Ryan Amos
Thomas的更正:从技术上讲,这不是四舍五入,而是由于浮点数的有限精度和二进制特性导致的不准确性。 - Ryan Amos
有人应该总结一下这个问题中的所有答案。 - SOFe
5个回答

33

不能使用等号判断浮点数是否相等的原因是浮点数不是完全精确的。有一些数字在存储时存在一定的不准确性,例如那些延伸到尾数和循环小数的数字(请注意,我说的是二进制下的循环小数)。你可以将这种不准确性看作是“向下取整”。超出浮点数精度的数字位被截断,实际上是进行了向下取整。

如果浮点数没有变化,它们之间的等式就会成立。但是,如果你稍微改变一下它们,你可能就不应该使用等式,而是使用类似于(x < 0.0001 && x > -0.0001)这样的范围。

简而言之:只要你不在一个非常小的水平上操作x,就没问题。


7
不是四舍五入的问题,而是由于浮点数的表示方式无法存储某些数字。0可以被浮点数值精确表示,但并非所有值都可以被精确表示。例如,0.1必须近似表示。 - Thomas Owens
1
@Thomas 向下取整。与3/2等于1的原因相同。 - Ryan Amos
8
不是四舍五入的问题,而是IEEE浮点数表示中的不精确性。完全与四舍五入无关。参见http://en.wikipedia.org/wiki/Floating_point#Accuracy_problems - Thomas Owens
@Thomas,同意您的观点。你认为 3/21,是因为“整数表示中的不精确性”吗?请参阅《计算机科学家应该了解的浮点运算》第173页或维基百科中的可表示数字、转换和舍入。 - Museful
5
是的,这完全与四舍五入无关。只不过它被称为“四舍五入”(即使在@Thomas提供的文章中也是如此)。 - Museful
显示剩余4条评论

17

如果你试图捕获的0是在初始化时设置的原始0,那么是安全的。但是,如果你期望从数学运算中得到一个0,则不安全。


当您想要比较数学运算中的0时,应该采取什么正确的方法? - vishnu viswanath

5

你仍然不应该检查它是否等于零,只需要检查它是否接近于零。

private final static double EPSILON = 0.00001;
if (x + EPSILON > 0 && x - EPSILON < 0){
...
}

3
你不应该使用绝对的 epsilon,而应该使用相对的 epsilon。例如,if (value >= target * (1 - epsilon) && value <= target * (1 + epsilon ))。特别是当值可以在大范围内变化时更应如此。 - Hovercraft Full Of Eels
2
@HovercraftFullOfEels 只有当目标值为正数时,这段代码才会起作用。 - Tanmay Patil

4

如果您自己设置它并希望查看其是否更改,可以安全地检查是否相等(例如使用哨兵值),但对于此类情况,NaN更加安全。

float x=Float.NaN;
{
   //some code that may or may not set x
}
if(Float.isNaN(x))//it never got set

1

double有正零和负零两种状态。正零和负零之间的==比较结果为false。此外,两个NaN之间的==比较结果也为false。


你错了。负零与正零相等。这是IEEE-794标准所要求的,而Java也遵循此标准。 - Sneftel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接