Workaround UB when checking if a double fits in int64_t?

Hello,

I want to check if a double can be losslessly convert to int64_t in a NaN-safe manner. It turns out that this is non-trivial, as casting NaN to integer is UB, and this UB actually fires under clang (https://godbolt.org/z/zje7Wxrrv):

bool check(double d)
{
    int64_t i = static_cast<int64_t>(d);
    return (d == static_cast<double>(i));
}

int main()
{
    if (check(std::numeric_limits<double>::quiet_NaN()))
    {
        printf("1");
    }
    else
    {
        printf("0");
    }
}

(program outputs 1 under -O3, expects 0)

And if I manually add a check for nan, it turns out that Clang cannot optimize it away and generates inferior code (see https://godbolt.org/z/ccGvEoM6M):

bool check(double d)
{
    if (d != d) { return false; }
    int64_t i = static_cast<int64_t>(d);
    return (d == static_cast<double>(i));
}

generates

check(double):                              # @check(double)
        cvttsd2si       rax, xmm0
        cvtsi2sd        xmm1, rax
        xorpd   xmm2, xmm2
        cmpordpd        xmm2, xmm0
        cmpeqpd xmm1, xmm0
        andpd   xmm1, xmm2
        movd    eax, xmm1
        and     al, 1
        ret

while all is needed actually is “cvttsd2si rax, xmm0; cvtsi2sd xmm1, rax”

How can I generate optimal code while still avoiding the UB? Thanks!

1 Like