15

In C you can test to see if a double is NaN using isnan(x). However many places online, including for example this SO answer say that you can simply use x!=x instead.

Is x!=x in any C specification as a method that is guaranteed to test if x is NaN? I can't find it myself and I would like my code to work with different compilers.

Simd
  • 19,447
  • 42
  • 136
  • 271
  • 2
    Are you still trying to find a way to optimize `isnan()`? Have you really benchmarked your application and discovered that this is the bottleneck? – Barmar Nov 25 '15 at 19:41

2 Answers2

11

NaN as the only value x with the property x!=x is an IEEE 754 guarantee. Whether it is a faithful test to recognize NaN in C boils down to how closely the representation of variables and the operations are mapped to IEEE 754 formats and operations in the compiler(s) you intend to use.

You should in particular worry about “excess precision” and the way compilers deal with it. Excess precision is what happens when the FPU only conveniently supports computations in a wider format than the compiler would like to use for float and double types. In this case computations can be made at the wider precision, and rounded to the type's precision when the compiler feels like it in an unpredictable way.

The C99 standard defined a way to handle this excess precision that preserved the property that only NaN was different from itself, but for a long time after 1999 (and even nowadays when the compiler's authors do not care), in presence of excess precision, x != x could possibly be true for any variable x that contains the finite result of a computation, if the compiler chooses to round the excess-precision result of the computation in-between the evaluation of the first x and the second x.

This report describes the dark times of compilers that made no effort to implement C99 (either because it wasn't 1999 yet or because they didn't care enough).

This 2008 post describes how GCC started to implement the C99 standard for excess precision in 2008. Before that, GCC could provide one with all the surprises described in the aforementioned report.

Of course, if the target platform does not implement IEEE 754 at all, a NaN value may not even exist, or exist and have different properties than specified by IEEE 754. The common cases are a compiler that implements IEEE 754 quite faithfully with FLT_EVAL_METHOD set to 0, 1 or 2 (all of which guarantee that x != x iff x is NaN), or a compiler with a non-standard implementation of excess precision, where x != x is not a reliable test for NaN.

Pascal Cuoq
  • 79,187
  • 7
  • 161
  • 281
  • 3
    I think your last point is key. `x != x` works for IEEE 754 floating point, but IEEE 754 is not mandated. Even existence of `NaN` itself is not guaranteed. – Adam Nov 25 '15 at 19:38
  • 3
    Does `isnan(x)` actually tell you, accurately, whether `x` is NaN on those platforms where `x != x` doesn't work for the same purpose? I'm imagining a compiler generating two different pieces of code for computing `x`; one for the `isnan(x)` call and one for whatever you're using it for inside that conditional. – tmyklebu Nov 26 '15 at 04:56
  • 1
    Put another way, is there a program using the folliowing function `f` that causes `f` to print out "0 is NaN" when built with such a compiler and then run? `void f(double x) { if (x != x) printf("%f is NaN\n"); }` – tmyklebu Nov 26 '15 at 05:01
5

Please refer to the normative section Annex F: IEC 60559 floating-point arithmetic of the C standard:

F.1 Introduction

An implementation that defines __STDC_IEC_559__ shall conform to the specifications in this annex.

Implementations that do not define __STDC_IEC_559__ are not required to conform to these specifications.

F.9.3 Relational operators

The expression x ≠ x is true if x is a NaN.

The expression x = x is false if X is a Nan.

F.3 Operators and functions

The isnan macro in <math.h> provides the isnan function recommended in the Appendix to IEC 60559.

Community
  • 1
  • 1
Kijewski
  • 25,517
  • 12
  • 101
  • 143
  • 1
    Do implementations actually define `__STDC_IEC_559__`, or is it like C++ versions where compilers don't want to claim to be compliant? – Adam Nov 25 '15 at 19:40
  • @Adam, valid question! gcc 5.2.1 20151028 does define it, clang 3.5.2-3 does not. For a x86_64-pc-linux-gnu target, that is. – Kijewski Nov 25 '15 at 19:43
  • 3
    @Adam in my experience C compilers may make claim through the definition of symbols that they do not respect in practice, for instance `clang -std=c99 -mno-sse2` claims `FLT_EVAL_METHOD` to be zero when it's not how it actually computes. I didn't think to check whether it defined `__STDC_IEC_559__`: http://stackoverflow.com/questions/17663780/is-there-a-document-describing-how-clang-handles-excess-floating-point-precision – Pascal Cuoq Nov 25 '15 at 19:45