Good sentinel value for double if prefer to use -ffast-math

c, double, fast-math, nan, optimization

Solution

If you are looking for a value which would be propagated by arithmetic operations, `NaN` is still available with option `-ffast-math`. The problem lies somewhere else. With `-ffast-math` some operations can removed from the computation due to optimization, and then there is no way to guarantee `NaN` or any other value would be propagates.

For example, the following, with `-ffast-math` set, will cause hard writing `0.0` into `n` and there is no special value for `n` which would protect from it.

float n = NAN;
n *= 0.0;

One thing you can do, is to use `-fno-finite-math-only -ftrapping-math` with `-ffast-math` as Shafik Yaghmour said. And the other is, if there are only few places where you expect a bad value, you can check for it by yourself putting tests exactly in those points.

The last option I can think -- if you really badly need optimization -- is to manually inject `NaN` (and maybe `inf`) values into the computation and check for how long it is propagated. Then in those places where the propagation stops, test for `NaN` (`inf`) occurrence. -- This is an unsafe method, as I am not one hundred percent sure, can `-ffast-math` involve conditional flow of operations. If it can, there is a significant chance, this solution will be invalid. So it is risky and if chosen needs very heavy testing covering all branches of the computation.

Normally I would be rather against the last solution, but actually there is a chance, `NaN` (`inf`) values will be propagated though the whole computation or almost whole, so it can give the performance you are seeking for. So you may want to take the risk.

Checking for `NaN` with `-ffast-math` you can do, as Shafik Yaghmour said, with

inline int isnan(float f)
{
    union { float f; uint32_t x; } u = { f };
    return (u.x << 1) > 0xff000000u;
}

and for `double` with

inline int isnan(double d)
{
    union { double d; uint64_t x; } u = { d };
    return (u.x << 1) > 0xff70000000000000ull;
}

Checking for `inf` would be

inline int isinf(float f)
{
    union { float f; uint32_t x; } u = { f };
    return (u.x << 1) == 0xff000000u;
}

inline int isinf(double d)
{
    union { double d; uint64_t x; } u = { d };
    return (u.x << 1) == 0xff70000000000000ull;
}

You can also merge `isnan` and `isinf`.

Problem

Since the `gcc` option `-ffast-math` effectively disables `NaN` and `-/+inf`, I'm looking for maybe the next best option for representing `NaN` in my performance-critical math code. Ideally the sentinel value if operated on (add, mul, div, sub, etc..) would yield the sentinel value as `NaN` would do but I doubt this would be possible since I think `NaN` is the only value that accomplishes this. `-0.0` might not be a good fit as it's also disabled in `-ffast-math` and could prevent certain optimizations like `(x+0.0)`, etc.. Perhaps my question should rather be, is there any way to use `NaN` or some other "special double" while being able to enable a lot of the math optimizations without breaking down? System is `Linux/x64, gcc 4.8.1`.

Original source

Related problems