cvisual-c++c99

Recent MSVC versions don't treat NAN as constant, workaround?


Recent MSVC versions don't seem to treat NAN as a constant anymore. The new definition appears to be (__ucrt_int_to_float(0x7FC00000)). The old one was (-(float)(((float)(1e+300 * 1e+300)) * 0.0F)).

This causes code like the following to fail to compile with error C2099: initializer is not a constant:

#include <math.h>

double x[] = { 1.0, NAN };

Unfortunately, I don't have direct access to MSVC and I am dealing with this through GitHub CI.

Questions:

Apparently, MSVC also doesn't accept 0.0 / 0.0. The code double f() { return 0.0 / 0.0; } fails with error C2124: divide or mod by zero


Solution

  • This is a bug in MSVC.

    The NAN macro is required to be a constant expression. Such expressions are allowed in file scope initializers as they can be evaluated at compile time.

    This is mandated in section 7.12p6 of the C99 standard:

    The macro

    NAN
    

    is defined if and only if the implementation supports quiet NaNs for the float type. It expands to a constant expression of type float representing a quiet NaN.

    The same language exists in C11 and C23.

    This it's the first time that MSVC has exhibited non standard compliant behavior.

    To work around this, you can check the MSVC version by inspecting the _MSC_VER macro. For MSVC 17.12, the internal version number is 19.42 which translates to a macro value of 1942. Based on this, you can redefine NAN to what is was previously.

    #include <math.h>
    
    #ifdef _MSC_VER
    # if _MSC_VER >= 1942
    #   undef NAN
    #   define NAN (-(float)(((float)(1e+300 * 1e+300)) * 0.0F))
    # endif
    #endif