Declaring 64-bit variables in C

I have a question.

uint64_t var = 1; // this is 000000...00001 right?

And in my code this works:

var ^ (1 << 43)

But how does it know 1 should be in 64 bits? Shouldn’t I write this instead?

var ^ ( (uint64_t) 1 << 43 )

As you supposed, 1 is a plain signed int (which probably on your platform is 32 bit wide in 2's complement arithmetic), and so is 43, so by any chance 1<<43 results in an overflow: in facts, if both arguments are of type int operator rules dictate that the result will be an int as well.

Still, in C signed integer overflow is undefined behavior, so in line of principle anything could happen. In your case, probably the compiler emitted code to perform that shift in a 64 bit register, so by luck it appears to work; to get a guaranteed-correct result you should use the second form you wrote, or, in alternative, specify 1 as an unsigned long long literal using the ull suffix (unsigned long long is guaranteed to be at least 64 bit).

var ^ ( 1ULL << 43 )

I recommend OP's approach, cast the constant ( (uint64_t) 1 << 43 )

For OP's small example, the 2 below will likely perform the same.

uint64_t var = 1; 
// OP solution)
var ^ ( (uint64_t) 1 << 43 )
// Others suggested answer
var ^ ( 1ULL << 43 )        

The above results have the same value, but different types. The potential difference lies in how 2 types exist in C: uint64_t and unsigned long long and what may follow.

uint64_t has an exact range 0 to 264-1.
unsigned long long has a range 0 to at least 264-1.

If unsigned long long will always be 64-bits, as it seems to be on many a machine there days, there is no issue, but let's look to the future and say this code was run on a machine where unsigned long long was 16 bytes (0 to at least 2128-1).

A contrived example below: The first result of the ^ is a uint64_t, when multiplied by 3, the product will still be uint64_t, performing a modulo 264, should overflow occur, then the result is assigned to d1. In the next case, the result of ^ is an unsigned long long and when multiplied by 3, the product may be bigger than 264 which is then assigned to d2. So d1 and d2 have a different answer.

double d1, d2;
d1 = 3*(var ^ ( (uint64_t) 1 << 43 ));
d2 = 3*(var ^ ( 1ULL << 43 ));

If one wants to work with unit64_t, be consistent. Do not assume unit64_t and unsigned long long are the same. If it is OK for your answer to be a unsigned long long, fine. But in my experience, if one starts using fixed sized types like uint64_t, one does not want variant size types messing up the computations.


var ^ ( 1ULL << 43 ) should do it.