我有以下功能:
template <typename Float, std::enable_if_t<std::is_floating_point_v<Float>, int> = 0>
constexpr Float inf2nan(Float x)
{
static_assert(std::numeric_limits<Float>::is_iec559);
return x * 0 + x;
}
如果输入无穷大,此函数将返回
NaN
,否则仅返回输入。
不幸的是,在 GCC 中使用 -ffast-math
标志会将其优化为仅 ret
语句。我希望我的函数在启用这些标志的情况下执行相同的操作。
我也尝试将其替换为:
return std::isinf(x) ? std::numeric_limits<Float>::quit_NaN() : x;
但这并没有被 GCC 优化,并且 clang 与我的函数具有相同的输出。
有没有一种方法(通过注释或宏)可以为单个变量或函数启用严格的浮点数学,类似于 Java 的
strictfp
关键字与 gcc 和 clang?或者,我可以检测到我的代码中启用了快速数学并有条件地编译后一个版本吗?
实现此目的的一种方法是将您想要为其拥有一组特定编译器标志的所有函数定义放入编译单元(.cpp 文件)中,并使用其自己的设置编译此编译单元。这样你就不需要依赖源设置中相关的编译器。
具体如何做到这一点取决于您的工具链。在 CMake 中,您可以创建一个
OBJECT
库,并将该 OBJECT
库与可执行文件链接。在 make 文件中,也应该直接这样做。
对于源内 gcc 特定解决方案,有:
现在大多数编译器都知道,在 -fast-math 下你不能用 fma() 做疯狂的事情,因为它会破坏算法。所以它不会打扰那些人。也就是说,使用 fma 的人这样做是有原因的,在这种情况下,这是为了精度,而不是您可能听说过的速度。所以,你可以尝试一下:
#include <math.h>
template <typename Float, std::enable_if_t<std::is_floating_point_v<Float>, int> = 0>
constexpr Float inf2nan(Float x)
{
static_assert(std::numeric_limits<Float>::is_iec559);
return fma(x, 0, x);
}
请注意,fma 在 Intel 处理器上会很慢,除非您打开了 Haswell 以及启用 Haswell 新指令功能集的更高版本的优化。不过,它在 GPU、powerpc、arm64 上速度很快,并且可能是比将函数移至另一个编译单元并关闭必要的链接选项更快的解决方案。
此外,作为棒球内部的一点,-fast-math 的起源是您可以为 SPEC 打开的单个编译器标志。它包括各种令人讨厌的内容,例如 -funsafe-optimizations。因此,除非你真的认为你赖以生存的应用程序应该附带用于 SPEC 身体部位测量竞赛的令人心潮澎湃的作弊优化,否则最好不要使用它。最好看看 -ffast-math 在哪里有帮助,看看你是否能找出原因并修复它,这样就不再需要该标志了。
也就是说,如果您正在使用 GPU,那里的文化会有所不同。信息?楠?谁在乎?这一切都只是像素!此外,您面前的硬件不一定是您将运行的硬件或驱动程序或公司,那么为什么要进行测试呢? FUnsafe对于每个人来说显然是“有趣”+“安全”! /s