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.