gcem icon indicating copy to clipboard operation
gcem copied to clipboard

_copysign cannot be used in constant context (MSVC 16 2019)

Open tomreddell opened this issue 5 years ago • 2 comments

Building GCEM in a project using Cmake on Windows 10, and with MSVC 16 2019 as the compiler fails, it appears that the implementation of _copysign cannot be used in a constant context. Many errors were thrown of the form:

C:\Users\61405\source\repos\BGNC\extern\gcem\include\gcem_incl/neg_zero.hpp(34,31): message : see usage of '_copysign' [C:\Users\61405\source\repos\BGNC\build\tests\BGNCTestExec.vcxproj]
C:\Users\61405\source\repos\BGNC\tests\coordinates_tests\earth_tests.cpp(129,28): error C2131: expression did not evaluate to a constant [C:\Users\61405\source\repos\BGNC\build\tests\BGNCTestExec.vcxproj]
C:\Users\61405\source\repos\BGNC\extern\gcem\include\gcem_incl/neg_zero.hpp(34,31): message : failure was caused by call of undefined function or one not declared 'constexpr' [C:\Users\61405\source\repos\BGNC\build\tests\BGNCTestExec.vcxproj]

The same code compiled fine when using GCC.

I've confirmed that the errors are removed by manually implementing a version of copysign:

#gcem_options.hpp

template <typename T>
constexpr T HACK_MSVCcopysign(T x, T y)
{
    if ((x < 0 && y > 0) || (x > 0 && y < 0))
    {
        return -x;
    }
    return x;
}

#ifdef _MSC_VER
    #ifndef GCEM_SIGNBIT
        #define GCEM_SIGNBIT(x) _signbit(x)
    #endif
    #ifndef GCEM_COPYSIGN
        // #define GCEM_COPYSIGN(x,y) _copysign(x,y)
        #define GCEM_COPYSIGN(x,y) HACK_MSVCcopysign(x, y)
    #endif
...

tomreddell avatar Jun 25 '20 07:06 tomreddell

Unfortunately, your approach does not cover signed zeros - try HACK_MSVCcopysign(1.0, -0.0).

If MSVC's _fpclass(x) function is constexpr friendly, then the new gcem::copysign function will provide a workaround.

kthohr avatar Aug 09 '20 20:08 kthohr

template <typename T>
constexpr bool signbit(const T x) noexcept
{
#if defined(__clang__) || defined(__GNUC__)
	return __builtin_signbit(x);
#else
	static_assert(sizeof(T) == 4 || sizeof(T) == 8);
	using uint = std::conditional_t<sizeof(T) == 4, std::uint32_t, std::uint64_t>;
	return std::bit_cast<uint>(x) & (uint{ 1 } << (sizeof(T) * 8 - 1));
#endif
}

lIIIIIIIIl avatar Sep 30 '20 12:09 lIIIIIIIIl