我正在做一个物理引擎,为了好玩.我试图使它可靠,即使在低tickrates,所以我做了一个危险的舞蹈与浮点算术和精度。
在调试的时候,我最终运行了这段代码。
#define A 0.2063387632369995100000000000000f
#define B 0.7307806611061096200000000000000f
float a = A;
float b = B;
float floatie1 = A + (B * ((A)/(-B)));
float floatie2 = a + (b * ((a)/(-b)));
printf("%.40f\n", floatie1);
printf("%.40f\n", floatie2);
输出结果是:
0.0000000149011611938476560000000000000000
0.0000000000000000000000000000000000000000
以及每个比特的位数(分别):
00110010100000000000000000000000
00000000000000000000000000000000
我知道这个表达式应该是计算为0 但我不想让它计算为0 因为我可能在代码中的某个地方有其他不相关的运算 会给我一个完全相同的不精确的值 如果我再减去2,它就会变成0 我不希望我的表达式在任意情况下被简化或优化
我试过让浮点数变得不稳定,并关闭优化,但无济于事。我也试过 -f-associative-math
, -ftrapping-math
和 -funsafe-math-optimizations
的组合,以及它们的 no-
变体。
如果我把表达式拆开,把东西放到不同的变量中,一次只做一件事,就可以了,但我不想每次写一段代码都要在背后看。
MSVC++给我的这段代码开箱即用的正确结果。
版本:Windows 8.1。gcc (MinGW.org GCC-6.3.0-1) 6.3.0
windows 8. 1. 如何关闭这个功能?
所有的功劳都归于 EOF.
我问错问题了。这不是真正的GCC简化表达式。
显然,GCC默认对32位进行了优化,使其使用更少的x87 FPU指令。尽管速度更快,但并不能保证符合IEEE 754的要求,因为在引擎盖下使用了双倍指令(EOF在他的 评论).
之所以结果不同,是因为GCC在编译时使用了符合IEEE 754标准的算子来计算第一个表达式的值,而对于第二个表达式(运行时),则使用了不符合标准的FPU指令。
为了避免这些指令(请记住这会使代码更慢),可以使用 -ffloat-store
作为编译选项。
或者,如果你不想让整个程序变得更慢,可以使用 杠杠 或函数属性。
void __attribute__((optimize ("-ffloat-store"))) func(float a, float b){
//your code
}
注:而不是 x87 FPU可以选择性地尝试将目标锁定在 上交所的扩展部分,它既符合IEEE 754标准,又是x86_64的基线。更多信息 此处.