比较浮点数及其副本

Comparing a float and its copy

我知道比较两个 doubles 是有问题的,如果它们是从不同的计算中获得的。但这是否也适用于其中一个是另一个的副本(价值)的情况。以下几行解释了该场景。如果我有这样的问题,

double a,b;
a=randdouble();/*some double value*/
b=a;

然后,

Q1) 如果是 C 编译器(我有 gcc 6.1.1),比较 a==b 是否总是保证 return true

Q2) 如果我使用 malloc 在堆内存中分配变量 ab,上面的答案会保持不变吗?

Q3) 如果我将 C 编译器替换为 JAVA 编译器(我正在使用 Open JDK 1.7.0)并进行必要的语法更改,上述答案是否会保持不变。

编辑 1:数字 ab!= NaN

Q1:不能保证比较结果为真,原因很简单,NaN 比较不等于自身。可能还有其他情况,但 NaN 是一个明显的反例。

Q2:变量在内存中的位置没有区别。

Q3:我希望 Java 的行为类似。

撇开这种特殊情况不谈,我相信标准并没有给出这样的保证:

想象一个 ABI,其中 double 表达式被求值,值被 return 以 80 位精度(英特尔 80x87 堆栈)编辑,但存储为 64 位 IEEE-754 双精度数。即使 randdouble() 被定义为 return a double,与 long double 相反,它的 return 值可能比存储到 [=16 中的值更精确=] 或 b。根据编译器如何优化 randdouble() 函数调用和比较 a == b 之间的各种表达式,它最终可能会将 80 位精确 return 值与其通过转换为获得的近亲值进行比较64 位和回到 80 位。如果在转换中丢失精度,比较将失败。我将尝试从标准中找到一个合适的参考来支持这一点,但这似乎是合理的,尽管 ab 是局部变量还是存储在堆上可能会影响顺序执行的转换,仍然不建议对一种或其他情况做出任何保证。

另一个答案说 NaN 总是不同的。这是 IEEE-754 浮点标准的定义。 C 和 Java 都将其用于浮点数和双位表示,因此将 NaN 视为不同的。

a=Double.NaN;
b=a;

if (a==b)   // <--- comparison will fail.

但对于所有其他值,比较的行为将根据值的 IEEE-754 位模式进行。如果两个变量的位表示相同,则比较结果为真。

因此对于您使用 randdouble() 的示例,您对 a==b 的比较将始终产生 true,因为您从字面上复制了 a 中的位表示以在 b 的赋值中(假设 randdouble() 永远不会 return NaN).

话虽这么说……您不应该依赖代码中浮点值的精确比较。像我们这里的简单示例一样,您的比较值很少是通过彼此的直接赋值得出的。它们通常是通过一些计算得出的。比较的每一方面通常都是通过一系列不同的计算得出的。由于 IEEE-754 限制固有的累积误差,计算通常会在位表示中产生略有不同的结果。

因此,内存位置也不重要,因为无论存储在何处,位模式都是相同的。

在 C 或 Java(或任何其他使用 IEEE-754 浮点表示的语言)之间也无关紧要。