I have a snippet of code that's present in a VS2017 static library. When linked in a 2017 executable, it works as expected. However, if linking into a 2022 executable, it breaks on the cast from double to uint64_t.
The double is below -1.0 so the truncated value is outside the value-range of uint64_t and ISO C doesn't define the behaviour, but MSVC does.
static uint64_t do_something_2(double f1)
{
return (uint64_t)(f1 - 0.5);
}
uint32_t do_something(void)
{
double f1 = -3406302.4613481420;
uint32_t f2 = (uint32_t)do_something_2(f1);
return f2;
}
When linking this into a 2022 executable, f2 is 0xffffffff, as if from double to uint64_t with AVX-512 vcvttsd2usi rax, xmm0 which produces all-ones for out-of-range values. (And then uint64_t to uint32_t truncation to get a 32-bit 0xffffffff value)
However, if I re-compile the static library using VS2022, I get my expected value of f2 = 4291560994 (0xffcc0622), as if (uint32_t)(uint64_t)double did get modulo reduction of the integer -3406302.
(For values that also fit in int64_t, we can get this result portably and efficiently with (uint64_t)(int64_t)double, especially in x64 code or with x87+SSE3 fisttp. But the question is why existing code written with (uint64_t)double doesn't compile the way I expected when mixing VS versions.)
When I compare the disassembly, I see the 2017-generated code just calls __dtoul3 and nothing more.
The 2022 generated assembly is much more involved with many more calls.
Afterwards, I edited my 2017 makefile by adding /arch=IA32 to disable SSE2 and then recompiled. This resulted in the correct value being computed.
So it seems to be an issue with the SSE2 code generation in VS2017 vs VS2022. However, this blog post on Microsoft seems to suggest that 2022 and 2017 should be compatible: https://devblogs.microsoft.com/cppblog/microsoft-visual-studio-2022-and-floating-point-to-integer-conversions/ It mentions changes to the default semantics for out-of-range FP to integer conversions to match AVX-512 instructions.
Only other things I'll add:
Tried compiling the library with 2019 and linking into the 2022 executable. Same issue as 2017, results in 0xFFFFFFF
The 2017 library linked into a 2017 executable does work.
Any idea why my findings seem to contradict Microsoft's blog post about floating point to integer conversions between 2017 and 2022?
A MSFT engineer explained the cause for this in a comment here:
https://developercommunity.visualstudio.com/t/Out-of-range-floating-point-to-integer-c/10324943#T-ND10356428