I want to set individual bits in a 64 bit number and figured that the uint64_t
is ideal for the job. I ran into some strange problems, so I did some experiments:
I wrote the following test program:
#include <iostream>
#include <cstdint>
#include <iomanip>
int main()
{
uint64_t test_value = 0;
for( int i=0; i<63; i ) {
test_value = (1 << i);
std::cout << hex << test_value << "\n";
}
return 0;
}
The output surprised me:
1
2
4
8
10
... (irrelevant output elided)
10000000
20000000
40000000
ffffffff80000000
1
2
4
8
10
...
10000000
20000000
40000000
I tested the max value (std::cout << hex << std::numeric_limits<uint64_t>::max() << std::endl
)
resulting in ffffffffffffffff
as expected.
I am running gcc 12.2.1 on Fedora 37.
It looks like my unsigned int changed to signed on bit 31 and rolled over on bit 32. Am I missing a compiler setting? Any help is greatly appreciated.
CodePudding user response:
Am I missing a compiler setting?
I think what you are missing is that 1
is an int
, and sizeof(int)
can be less than sizeof(uint64_t)
. Shifting a 32-bit value left by more than 31 bits yields results that aren't particularly useful.
If you replace 1
with 1ULL
, or alternatively with ((uint64_t)1)
, you will get behavior that is more in line with your expectations.
CodePudding user response:
Your compiler should have warned you.
In this line:
test_value = (1 << i);
1
is an int
literal (and int
is usully 32 bit).
E.g. in MSVC I get:
warning C4334: '<<': result of 32-bit shift implicitly converted to 64 bits (was 64-bit shift intended?)
You should change it to:
//-------------vvv-------
test_value = (1ull << i); // You can also use: (uint64_t)1 instead of 1ull
And now you'll get the output you expect.