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 )
Microsoft C/C++ features support for sized integer types. You can declare 8-, 16-, 32-, or 64-bit integer variables by using the __intN type specifier, where N is 8, 16, 32, or 64.
The long long data type makes handling 64 bit integers easy. In C language, an unsigned number over 32 bits cannot exceed the value of 4,294,967,295. You may find you are required to handle larger numbers and for this you need these numbers to be coded in 64-bit.
You can use uint64_t type and "%" SCNu64 format specifier for unsigned 64-bit integer.
long , ptr , and off_t are all 64 bits (8 bytes) in size. The 32-bit data model for z/OS® XL C/C++ compilers is ILP32 plus long long.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With