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