After reading all of the questions and answers on bit shifting/masking, I simply cannot wrap my head around it. I'm just not understanding how it works on a fundamental level. I've been able to achieve various techniques by using BitArray and BitConverter instead, but I really would like to understand bit shifting/masking better.
The specific need I have is to do the following:
I have a ushort: 0x810E (33038)
Using bit shifting/masking, I'd like to know how to:
- Get the 16th bit Result: 1
- Get the 15th bit Result: 0
- Get a range of bits to create a new ushort value, specifically the
first 14 bits Result: 270
As I said, I'm able to perform these tasks using BitArray which is how I've gotten the correct results, but I'd like to understand how to perform these operations using bit shifting/masking.
Any help would be appreciated.
Masking single bits
As you probably know, ushort
is a 16-bit value, so your given number 0x810E
can also be written as
10000001 00001110
Because there is no shifting operator for ushort
, the value is first converted to int
.
So, if you want to get the 15th bit, you can take a single bit
000000000 0000000 00000000 00000001
and shift it 14 times to the left (right side is filled with 0
)
00000000 00000000 01000000 00000000
and you have created a bitmask.
Now if you combine mask and value with an bitwise and
, you'll get only the value of the 15th bit:
00000000 00000000 10000001 00001110
& 00000000 00000000 01000000 00000000
= 00000000 00000000 00000000 00000000
which is 0
again. To access this bit, you'll have to shift the whole result back to the right 14 times and cast it to a ushort
.
This can be expressed with the following code:
ushort value_15 = (ushort)(((1 << 14) & value) >> 14);
Can we do better?
Although this approach seems to be correct, but there is a simpler way to do this: shift the original value to the right 14 times
(result is 00000000 00000000 00000000 00000010
, left side is filled with 0
) and perform a simple bitwise &
with 1
:
00000000 00000000 00000000 00000000 00000000 00000010
& 00000000 00000000 00000000 00000000 00000000 00000001
= 00000000 00000000 00000000 00000000 00000000 00000000
This results in C#
in:
ushort value_15 = (ushort)((value >> 14) & 1);
So you avoid one additional shift and we get the same results even when using
signed numbers (because there the highest bit, used for the sign, is left unchanged by shifting).
Masking a bit range
To mask a bit range, all you have to do is to change your mask. So, to get the value of the lower 14 bits you can use the mask
00000000 00000000 10000001 00001110
& 00000000 00000000 00111111 11111111
= 00000000 00000000 00000001 00001110
In C#
this can be expressed with
ushort first14bits = (ushort)((0xFFFF >> 2) & value);
where (0xFFFF
is 00000000 00000000 11111111 11111111
).