我知道,如果两个doubles
是从不同的计算中获得的,那么比较它们是有问题的。但是,当其中一个是另一个的副本(价值)时,这也适用吗。以下几行解释了该场景。如果我有这样的问题,
double a,b;
a=randdouble();/*some double value*/
b=a;
然后,
Q1)在C编译器(我有gcc 6.1.1
)的情况下,比较a==b
是否总是保证返回true
?
Q2)如果我使用malloc
在堆内存中分配变量a
和b
,上面的答案会保持不变吗?
Q3)如果我用JAVA编译器(我使用的是Open JDK 1.7.0
)替换C编译器,并进行必要的语法更改,上面的答案会保持不变吗。
编辑1:数字a
和b
是!= NaN
Q1:不能保证比较结果为真,原因很简单,NaN
与自身比较不相等。可能还有其他情况,但NaN
是一个明显的反例。
Q2:变量在内存中的位置没有区别。
Q3:我希望Java也能有类似的表现。
撇开这种特殊情况不谈,我相信标准并没有给出这样的保证:
想象一个ABI,其中计算double
表达式,并以80位精度(Intel 80x87堆栈)返回值,但存储为64位IEEE-754双精度。即使randdouble()
被定义为返回double
,而不是long double
,其返回值也可能比存储在a
或b
中的值更精确。根据编译器如何优化randdouble()
函数调用和比较a == b
之间的各种表达式,它最终可能会将80位的精确返回值与其通过转换为64位并返回到80位而获得的近亲进行比较。如果转换中丢失了精度,则比较将失败。我将尝试从标准中找到一个适当的参考来支持这一点,但这似乎是合理的,尽管a
或b
是局部变量还是存储在堆中可能会对执行的转换顺序产生影响,但对其中一种或其他情况采取任何保证都是不明智的。
正如另一个答案所说,NaN总是保证不同的。这是IEEE-754浮点标准的定义。C和Java都将其用于浮点和双位表示,因此将NaN视为不同的。
a=Double.NaN;
b=a;
if (a==b) // <--- comparison will fail.
但是对于所有其他值,该比较将根据该值的IEEE-754比特模式来表现。如果两个变量的位表示相同,则该比较将产生true。
因此,对于您使用randdouble()的示例,您对a==b的比较将始终为true,因为您从a中实际复制了位表示形式以用于b的赋值(假设randdouble)永远不会返回NaN)。
话虽如此。。。您永远不应该依赖于代码中浮点值的精确比较。在我们这里的琐碎示例中,通过彼此直接赋值来获得比较值是非常罕见的。它们通常是通过一些计算得出的。比较的每一面往往是通过一系列不同的计算得出的。由于IEEE-754限制所固有的累积误差,计算常常在比特表示中产生略微不同的结果。
因此,内存位置也无关紧要,因为无论存储在哪里,位模式都是相同的。
这在C或Java(或任何其他使用IEEE-754浮点表示的语言)之间也无关紧要。