【发布时间】:2020-05-15 22:28:48
【问题描述】:
我正在制作一个有趣的物理引擎。 我试图让它即使在低滴答率下也很可靠,所以我在浮点算术和精度上做着危险的舞蹈。
在调试时,我最终运行了这段代码:
#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。
我不希望它的计算结果为零,因为我可能在代码中的某处有一些其他不相关的算术,它会给我一个以完全相同的方式不精确的值,如果我然后减去 2,它将是 0。
我不希望我的表达在任意情况下被简化或优化。
我尝试过使浮点数不稳定并关闭优化无济于事。
我也尝试过-f-associative-math、-ftrapping-math 和-funsafe-math-optimizations,以及它们的no- 变体的组合。
如果我分解表达式,将内容放入不同的变量中,一次只做一件事,它会起作用,但我不想每次写一段代码时都背着我。
MSVC++ 使用此代码为我提供了正确的结果。
版本:gcc (MinGW.org GCC-6.3.0-1) 6.3.0 windows 8.1。
如何关闭此功能?
【问题讨论】:
-
你为什么不为此使用一个理智的函数,比如
remainderf(A, B)? -
@EOF 因为这与该表达式(或派生该表达式的原始表达式)应该做什么无关。
-
这里有几点需要说明。首先,你是为 32 位编译的吗?如果是这样,一个很好的猜测是问题源于可恶的 x87 FPU,默认情况下它在 32 位模式下使用,并且不(完全)符合 ieee754。在为 x86_64 Linux 编译
gcc时,我无法重现您的问题。您自己编写的不稳定的remainderf()会为编译时和运行时计算产生相同(不稳定)的结果,而库remainderf()会产生正确的结果。 -
如果这不是另一个x87脑损伤的受害者,我会感到惊讶。在编译时,
gcc尊重 ieee754,因为为什么不呢,那时它不会花费任何成本。在运行时,x87 FPU 需要 可怕 的性能成本才能符合 ieee754,因为要真正以float精度进行计算,每个中间结果都必须存储到内存中并随后重新加载以继续计算。太疯狂了,编译器通常只在必须时才这样做。您可以通过将每个中间结果存储到变量(提示、提示)或将ffloat-store传递给gcc来强制它。 -
您可以通过一些
#pragma push #pragma GCC optimize (STRING, ...) [...] #pragma pop或__attribute__((optimize (STRING, ...)))魔法启用-ffloat-store-deoptimization。
标签: c++ gcc floating-point g++ precision