比较浮点数及其副本
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
在堆内存中分配变量 a
和 b
,上面的答案会保持不变吗?
Q3) 如果我将 C 编译器替换为 JAVA 编译器(我正在使用 Open JDK 1.7.0
)并进行必要的语法更改,上述答案是否会保持不变。
编辑 1:数字 a
和 b
是 != 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 位。如果在转换中丢失精度,比较将失败。我将尝试从标准中找到一个合适的参考来支持这一点,但这似乎是合理的,尽管 a
或 b
是局部变量还是存储在堆上可能会影响顺序执行的转换,仍然不建议对一种或其他情况做出任何保证。
另一个答案说 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 浮点表示的语言)之间也无关紧要。
我知道比较两个 doubles
是有问题的,如果它们是从不同的计算中获得的。但这是否也适用于其中一个是另一个的副本(价值)的情况。以下几行解释了该场景。如果我有这样的问题,
double a,b;
a=randdouble();/*some double value*/
b=a;
然后,
Q1) 如果是 C 编译器(我有 gcc 6.1.1
),比较 a==b
是否总是保证 return true
?
Q2) 如果我使用 malloc
在堆内存中分配变量 a
和 b
,上面的答案会保持不变吗?
Q3) 如果我将 C 编译器替换为 JAVA 编译器(我正在使用 Open JDK 1.7.0
)并进行必要的语法更改,上述答案是否会保持不变。
编辑 1:数字 a
和 b
是 != 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 位。如果在转换中丢失精度,比较将失败。我将尝试从标准中找到一个合适的参考来支持这一点,但这似乎是合理的,尽管 a
或 b
是局部变量还是存储在堆上可能会影响顺序执行的转换,仍然不建议对一种或其他情况做出任何保证。
另一个答案说 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 浮点表示的语言)之间也无关紧要。