Consider the following code:
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
long double test = 0xFFFFFFFFFFFFFFFF;
cout << "1: " << test << endl;
unsigned long long test2 = test;
cout << "2: " << test2 << endl;
cout << "3: " << (unsigned long long)test << endl;
return 0;
}
Compiling this code with GCC g (7.5.0) and running produces the following output as expected:
1: 1.84467e 19
2: 18446744073709551615
3: 18446744073709551615
However compiling this with the Microsoft Visual C compiler (16.8.31019.35, both 64-bit and 32-bit) and running produces the following output:
1: 1.84467e 19
2: 9223372036854775808
3: 9223372036854775808
When casting a value to an unsigned long long
, the MSVC compiler won't give a value lager than the max of a (signed) long long
.
Am I doing something wrong?
Am I running into a compiler limitation that I do not know about?
Does anyone know of a possible workaround to this problem?
CodePudding user response:
Because a MSVC long double
is really just a double
(as pointed out by @drescherjm in the comments), it does not have enough precision to contain the exact value of 0xFFFFFFFFFFFFFFFF. When this value is stored in the long double
it gets "rounded" to a value that is lager than 0xFFFFFFFFFFFFFFFF. This then causes undefined behaviour when converting to an unsigned long long
.
CodePudding user response:
You are seeing undefined behaviour because, as pointed out in the comments, a long double
is the same as a double
in MSVC and the 'converted' value of your 0xFFFFFFFFFFFFFFFF
(or ULLONG_MAX
) actually gets 'rounded' to s lightly larger value, as can be seen in the following code:
int main(int argc, char* argv[])
{
long double test = 0xFFFFFFFFFFFFFFFF;
cout << 0xFFFFFFFFFFFFFFFFuLL << endl;
cout << fixed << setprecision(16) << endl;
cout << test << endl;
return 0;
}
Output:
18446744073709551615
18446744073709551616.0000000000000000
Thus, when converting that floating-point value back to an unsigned long long
, you are falling foul of the conversion rules specified in this Microsoft document:
- For conversion to
unsigned long
orunsigned long long
, the result of converting an out-of-range value may be some value other than the highest or lowest representable value. Whether the result is a sentinel or saturated value or not depends on the compiler options and target architecture. Future compiler releases may return a saturated or sentinel value instead.
This UB can be further 'verified' (for want of a better term) by switching to the clang-cl compiler that can be used from within Visual Studio. For your original code, this then gives 0
for the values on both the "2" and "3" output lines.