#include <cstdio>
int main(void)
{
int val = 500;
printf("%d\n", (int)((long double)val / 500));
printf("%d\n", (int)((long double)500 / 500));
}
很明显,它应该输出 1 1
. 但如果你用 -Ofast
,它将输出 0 1
为什么?
如果你改变 500
到其他值(如 400
),并以 -Ofast
,它仍然会输出 1 1
.
编译器探索器与 -Ofast
: https:/gcc.godbolt.orgzYkX7fB
似乎是这一行导致了这个问题。
有了 -Ofast
, -ffast-math
启用,这可能会导致一些操作以不同和更快的方式计算。在你的情况下。(long double)val / 500)
可计算为 (long double)val * (1.0L / 500))
. 这可以在生成的程序集中看到,当你比较一下 -O2
和 -Ofast
为以下功能。
long double f(long double a)
{
return a / 500.0L;
}
生成的装配体 -O2
涉及 fdiv
指令,而用 -Ofast
涉及 fmul
指示,见 https:/gcc.godbolt.orgz58VHxb.
其次,1500,也就是0.002,是不能用 long double
正好。因此,会出现一些四舍五入的情况,而在你的情况下,这种四舍五入恰好是向下的。这可以通过以下表达式来检查。
500.0L * (1.0L / 500.0L) < 1.0L
可以用以下表达式来检查: true
: https:/gcc.godbolt.orgzzMcjxJ。. 所以,准确的存储乘数是0.002------。一些极小的三角洲.
最后,乘法的结果是500*(0.002-)。三角洲) = 1 - 小意思. 而当这个值转换成 int
,它被截断了,因此结果是在 int
是0。
-Ofast
无视严格的标准遵守。
-Ofast
使所有-O3
优化。它还可以实现并非对所有符合标准的程序有效的优化。它开启了-ffast-math
,-fallow-store-data-races
和Fortran专用的[...]。
-ffast-math
设置选项
-fno-math-errno
,-funsafe-math-optimizations
,-ffinite-math-only
,-fno-rounding-math
,-fno-signaling-nans
,-fcx-limited-range
和-fexcess-precision=fast
.该选项会使预处理程序宏
__FAST_MATH__
将被定义。此选项不会被任何
-O
选项-Ofast
因为对于依赖于IEEE或ISO数学函数规则规范的精确实现的程序来说,它可能会导致不正确的输出。然而,对于不需要这些规范保证的程序,它可能会产生更快的代码。
结论:不要使用 不要使用 -ffast-math
除非你愿意得到像现在这样的惊喜。