我知道使用==
检查浮点变量的相等性不是一个好方法。但是我只想通过以下语句知道这一点:
float x = ...
float y = x;
assert(y == x)
既然y
是从复制x
,则断言是否正确?
-m32
)或指示GCC使用x87 FPU(-mfpmath=387
)进行复制。
我知道使用==
检查浮点变量的相等性不是一个好方法。但是我只想通过以下语句知道这一点:
float x = ...
float y = x;
assert(y == x)
既然y
是从复制x
,则断言是否正确?
-m32
)或指示GCC使用x87 FPU(-mfpmath=387
)进行复制。
Answers:
除了assert(NaN==NaN);
kmdreko指出的情况外,还可以使用x87-math的情况,即将80位浮点数临时存储到内存中,然后与仍存储在寄存器中的值进行比较。
可能的最小示例,使用以下命令编译时,该示例在gcc9.2上失败-O2 -m32
:
#include <cassert>
int main(int argc, char**){
float x = 1.f/(argc+2);
volatile float y = x;
assert(x==y);
}
Godbolt演示: https
的 volatile
大概可以忽略,如果你管理,以产生足够的寄存器压力已经y
存储并从内存重新加载(但迷惑编译器就够了,不要省略比较全在一起)。
请参阅GCC常见问题解答参考:
float
具有标准精度的a 与额外精度进行比较时会考虑额外的位,这似乎很奇怪。
-ffloat-store
似乎是防止这种情况的方法。
如果x
为is NaN
,则不会是正确的,因为on的NaN
比较总是错误的(是,甚至NaN == NaN
)。对于所有其他情况(正常值,次正常值,无穷大,零),此断言将为真。
避免==
浮点数的建议适用于计算,因为浮点数在算术表达式中使用时无法准确地表达许多结果。分配不是计算,因此没有理由分配的值会与原始值不同。
如果遵循该标准,则不应该进行扩展精度评估。从<cfloat>
继承自C [5.2.4.2.2.8](强调我的):
除赋值和强制转换(除去所有额外的范围和精度)外,带有浮点操作数的运算值和需要进行常规算术转换的值以及浮点常量的值将求值为其范围和精度可能大于整数所要求的格式。类型。
但是,正如评论所指出的那样,某些情况下使用某些编译器,build-options和target 可能会使此矛盾。
x
在第一行的寄存器中计算if ,则保持比a的最小值更高的精度float
。该y = x
可以在内存中,只保留float
精度。然后,将针对寄存器对存储器进行相等性测试,精度不同,因此无法保证。
x+pow(b,2)==x+pow(a,3)
auto one=x+pow(b,2); auto two=y+pow(a,3); one==two
可能与之不同,因为一个人可能比另一个人使用更高的精度进行比较(如果一个/两个是ram中的64位值,而中间值是fpu上的80位位)。因此,有时分配可以做些事情。
gcc -ffloat-store
严格遵守。但是这个问题是关于x=y; x==y;
两者之间的任何变化都不做任何事情。 如果y
已经舍入为适合浮点数的值,则转换为double或long double并返回不会更改该值。...
是的,y
肯定会采用x
:
[expr.ass]/2
:在简单赋值(=)中,通过用右操作数的结果替换其值来修改([defns.access])左操作数所引用的对象。
没有其他分配值的余地。
(其他人已经指出,等效比较==
仍将评估false
为NaN值。)
浮点数的常见问题==
是,很容易没有您认为的价值。在这里,我们知道两个值,无论它们是什么,都是相同的。
[expr]
。如果我忽略链接并专注于引用,那么我会感到困惑,例如C.5.3似乎并没有解决术语“值”或“结果”的使用(尽管确实如此)在正常的英语环境中使用“结果”一次)。也许您可以更清楚地描述您认为标准在什么地方与众不同,并对此事件提供一个明确的引用。谢谢!
是的,在所有情况下(不考虑NaN和x87问题),这都是正确的。
如果memcmp
对它们进行操作,则可以测试是否相等,同时可以比较NaN和sNaN。这也将要求编译器获取变量的地址,该地址会将值强制转换为32位float
而不是80位。这将消除x87问题。这里的第二个断言旨在不表明==
不会将NaNs比较为true:
#include <cmath>
#include <cassert>
#include <cstring>
int main(void)
{
float x = std::nan("");
float y = x;
assert(!std::memcmp(&y, &x, sizeof(float)));
assert(y == x);
return 0;
}
请注意,如果NaN具有不同的内部表示形式(即尾数不同),memcmp
则不会比较true。
在通常情况下,它将评估为true。(否则断言语句不会做任何事情)
编辑:
“通常情况”是指排除其他用户指出的上述方案(例如NaN值和80x87浮点数单元)。
考虑到在当今情况下8087芯片已经过时,这个问题相当孤立,该问题适用于当前使用的浮点架构状态,除了NaN之外,所有情况都适用。
(参考约8087- https: //home.deec.uc.pt/~jlobo/tc/artofasm/ch14/ch143.htm)
感谢@chtz再现了一个很好的例子,@ kmdreko提到了NaNs-以前不知道它们!
x
位于浮点寄存器中y
。内存的精度可能不如寄存器,导致比较失败。
float
没有额外精度的值。
int a=1; int b=a; assert( a==b );
可以引发一个断言,我认为只有在正确运行一个编译器的情况下回答这个问题才有意义(尽管可能会注意到某些版本的编译器确实/具有-知道-弄错了)。实际上,如果出于某种原因,编译器没有从寄存器存储的赋值结果中消除额外的精度,则应在使用该值之前这样做。