1

I have the following function:

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;
}

This function will return NaN if the input is infinity, otherwise just the input. Unfortunately, using the -ffast-math flag with GCC optimizes this away to just a ret statement. I want my function to do the same with these flags enabled.

I've also tried replacing it with:

return std::isinf(x) ? std::numeric_limits<Float>::quit_NaN() : x;

but this does not get optimized by GCC and clang to the same output as my original function.

Is there a way (via comment or macro) to enable strict floating point math for just a single variable or function similar to Java's strictfp keyword with GCC and clang? Alternatively, can I detect that fast math was enabled in my code and conditionally compile the latter version?

Jan Schultke
  • 17,446
  • 6
  • 47
  • 96

2 Answers2

3

One way how to do that is to place all function definitions for which you want to have a certain set of compiler flags into on compilation unit (.cpp file) and compile this compilation unit with its own settings. That way you do not need to rely on compiler related in source settings.

How to exactly do that depends on your toolchain. In CMake you could create an OBJECT library, and link that OBJECT library with your executable. In a make file, it should also be straight forward to do that.

For in-source gcc specific solutions there is:

t.niese
  • 39,256
  • 9
  • 74
  • 101
0

Most compilers these days have learned that under -fast-math you can't do crazy stuff with fma(), because it breaks algorithms. So it will leave those alone. That is, people who use fma do so FOR A REASON, and in such cases, it is for precision instead of what you might have heard, speed. So, you might give this a try:

#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);
}

Note that fma will be slow on Intel processors unless you've turned on Haswell and later optimizations that enable the Haswell New Instructions feature set / AVX2. It will be fast on a GPU, powerpc, arm64 though, and probably a faster solution there than moving your function off to another compilation unit and turning off the requisite link optimizations.

Also, as a bit of inside baseball, the genesis of -fast-math was the single compiler flag you could turn on for SPEC. It includes 12 kinds of nasty, such as -funsafe-optimizations. So unless you really think your app that your livelihood is based on should ship with the chest thumping cheatership optimizations intended for the SPEC body part measuring contest, it might be best left off. Better to look at where -ffast-math is helping and see if you can figure out why and fix that so the flag isn't needed anymore.

That said, if you are working on a GPU, the culture is different over there. Inf? Nan? Who cares? It's all just pixels! Also, the hardware sitting in front of you isn't necessarily the hardware or driver or company you'll be running on, so why test? FUnsafe is clearly "fun"+"safe" for everyone! /s