-当使用长双数时,Ofast产生不正确的代码。

问题描述 投票:2回答:1
#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

似乎是这一行导致了这个问题。

enter image description here

c++ c compilation g++ compiler-optimization
1个回答
2
投票

有了 -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。


3
投票

-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 除非你愿意得到像现在这样的惊喜。

© www.soinside.com 2019 - 2024. All rights reserved.