#1563: Provide always-working isnan etc. --------------------------+------------------------------------------------- Reporter: eschnett | Owner: Type: enhancement | Status: reopened Priority: major | Milestone: Component: Cactus | Version: development version Resolution: | Keywords: --------------------------+-------------------------------------------------
Comment (by eschnett):
Compiler optimizations are usually not specified via a set of transformations that are allowed, but rather via a set of properties that the compiler can assume are valid in any program it sees, and that it needs to preserve when it generates machine code. By default, such a property is that the source code adheres to the IEEE floating point standard, and the compiler thus preserves the meaning of the source code. (We all know that this standard is too strict for us.)
Thus there is -ffast-math, which means that we use a looser floating point standard where we do not care about "proper" behaviour of nans. For example, fmax(nan, 1.0) should return 1.0 ("man fmax"). Implementing this correctly requires checking each argument whether it is nan before actually performing the comparison to see which argument is larger, as in: {{{ inline double fmax(double x, double y) { if (isnan(x)) return y; if (isnan(y)) return x; return x>=y ? x : y; } }}} Since we want fast code, we tell the compiler that we don't care about correct behaviour for nans. One of the easiest and most systematic ways to do this is to simply say that isnan and isinf never return true. This will automatically avoid all special cases for inf and nan, leading to faster code.
Of course you may say "I only want to optimize out the isnan calls that I didn't write myself", but that is difficult to judge for the compiler. The compiler -- in particular in C++, where almost all of the standard library exists as header files that are compiled as needed, together with the application code -- cannot tell whether a particular isnan call has been inserted into a standard function (and should be omitted), or has been inserted into a error-checking function (and needs to be kept). For the compiler, either nans need to be treated as specified in the source code (keep the isnan calls), or whether it can optimize out the nan-handling cases (assume isnan is always false).
One could, conceivably, have two kinds of isnan calls: the "annoying" once in fmax that should be omitted, and the "important" ones that we write in NaNChecker. Alas, the C and C++ standards don't make that distinction, they only specify one kind of isnan, namely the one that should always be kept. There is not even a proper specification of floating point numbers when -ffast-math is used. Thus, we need to roll our own "never-remove" isnan calls.
We could try to find compiler options (that's highly compiler specific) that ignore certain IEEE properties (associativity, reciprocal) while keeping others (nan behaviour of fmax etc.). I don't think it's worthwhile.