I found that in some answers they recommended using
lower = (some_var << 32) >> 32;
But I tested and found the following is faster:
lower = some_var & 0xffffffff;
So which is better? Is the former safer in some cases or faster after compiler optimized?
A 32 bit Signed Integer can house a number from −2,147,483,648 to 2,147,483,647 Unsigned: 0 to 4,294,967,295. A 64 bit Signed Integer can house a number from −9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 Unsigned: 0 to 18,446,744,073,709,551,615.
A 32-bit number can store 2^32 values, or 4,294,967,296. Meanwhile, a 64-bit number has 2^64 possible values, or a staggering 18,446,744,073,709,551,616.
An unsigned integer is a 32-bit datum that encodes a nonnegative integer in the range [0 to 4294967295]. The signed integer is represented in twos complement notation.
Masking with &
is better:
&
is reliable for signed and unsigned some_var
, while bitshifting right a negative number produces an implementation defined result:The value of E1 >> E2 is E1 right-shifted E2 bit positions. [...] If E1 has a signed type and a negative value, the resulting value is implementation-defined.
The one disadvantage of masking is that it's relatively easy to accidentally have 7 or 9 F
s, whereas a typo in 32
is obvious: there are other ways to generate the masking value though, e.g. (1LL<<32)-1
, or the hackish but somehow elegant uint32_t(-1)
.
Of course, if lower
is uint32_t
and some_var
uint64_t
, you can just let the conversion be implicit, so the optimiser doesn't even need to realise the bitwise-AND can be removed before assignment, but that might give you a compiler warning, which you can silence ala...
uint32_t lower = static_cast<uint32_t>(some_var);
The masking is mainly useful when assigning to another uint64_t
, or when the mask isn't for all the 32 least significant bits.
The masking with AND is better since it doesn't depend on the signedness of the value.
But the most efficient way to take the lower 32 bit is to assign it to a 32-bit variable.
uint64_t u = 0x1122334455667788;
uint32_t n;
n = static_cast<uint32_t>(u); // 0x55667788
The difference to a bit-wise AND is that the CPU just takes the lower part without doing any logical operation.
If you have a 32-bit CPU it just ignores the upper value stored in a second register or memory place.
If you have a 64-bit CPU it has a single instruction to extend (unsigned) a 32 bit value to 64 bit value.
A good optimizer would generate the same code in both cases. To me this is the most straight forward method: lower = some_var & 0xffffffff;
The other form may generate unnecessary shiftage.
Sometimes I use union to overlap variables when I want to be absolutely sure the compiler doesn't mess things up.
For example:
typedef union {
int64 QWORD;
int32 DWORD[2];
} overlapper64;
overlapper someVariable;
Then access it as:
someVariable.QWORD;
int32 myVar32 = someVariable.DWORD[0];
Depending on platform/compiler the order in which the overlap occurs may vary. Be sure to test it on your specific platform. In C, I use a bunch of platform specific #ifdefs to control the order automatically.
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