You can set the fourth bit of a number by OR-ing it with a value that is zero everywhere except in the fourth bit. This could be done as
x |= (1u << 3);
Similarly, you can clear the fourth bit by AND-ing it with a value that is one everywhere except in the fourth bit. For example:
x &= ~(1u << 3);
Finally, you can toggle the fourth bit by XOR-ing it with a value that is zero everywhere except in the fourth bit:
x ^= (1u << 3);
To see why this works, we need to look at two things:
- What is the behavior of the
<<
operator in this context?
- What is the behavior of the AND, OR, and XOR operators here?
In all three of the above code snippets, we used the <<
operator to generate a value. The <<
operator is the bitwise shift-left operator, which takes a value and then shifts all of its bits some number of steps to the left. In your case, I used
1u << 3
to take the value 1 (which has binary representation 1) and to then shift all its bits over three spots, filling in the missing values with 0. This creates the binary value 1000
, which has a bit set in the fourth bit.
Now, why does
x |= (1u << 3);
set the fourth bit of the number? This has to do with how the OR operator works. The |=
operator is like +=
or *=
except for bitwise OR - it's equivalent to
x = x | (1u << 3);
So why does OR-ing x with the binary value 1000
set its fourth bit? This has to do with the way that OR is defined:
0 | 0 == 0
0 | 1 == 1
1 | 0 == 1
1 | 1 == 1
More importantly, though, we can rewrite this more compactly as
x | 0 == x
x | 1 == 1
This is an extremely important fact, because it means that OR-ing any bit with zero doesn't change the bit's value, while OR-ing any bit with 1 always sets that bit to one. This means that when we write
x |= (1u << 3);
since (1u << 3) is a value that is zero everywhere except in the fourth bit, the bitwise OR leaves all the bits of x unchanged except for the fourth bit, which is then set to one. More generally, OR-ing a number with a value that is a series of zeros and ones will preserve all the values where the bits are zero and set all of the values where the bits are one.
Now, let's look at
x &= ~(1u << 3);
This uses the bitwise complement operator ~
, which takes a number and flips all of its bits. If we assume that integers are two bytes (just for simplicity), this means that the actual encoding of (1u << 3)
is
0000000000001000
When we take the complement of this, we get the number
1111111111110111
Now, let's see what happens when we bitwise AND two values together. The AND operator has this interesting truth table:
0 & 0 == 0
0 & 1 == 0
1 & 0 == 0
1 & 1 == 1
Or, more compactly:
x & 0 == 0
x & 1 == x
Notice that this means that if we AND two numbers together, the resulting value will be such that all of the bits AND-ed with zero are set to zero, while all other bits are preserved. This means that if we AND with
~(1u << 3)
we are AND-ing with
1111111111110111
So by our above table, this means "keep all of the bits, except for the fourth bit, as-is, and then change the fourth bit to be zero."
More generally, if you want to clear a set of bits, create a number that is one everywhere you want to keep the bits unchanged and zero where you want to clear the bits.
Finally, let's see why
x ^= (1u << 3)
Flips the fourth bit of the number. This is because the binary XOR operator has this truth table:
0 ^ 0 == 0
0 ^ 1 == 1
1 ^ 0 == 1
1 ^ 1 == 0
Notice that
x ^ 0 == 0
x ^ 1 == ~x
Where ~x
is the opposite of x; it's 0 for 1 and 1 for 0. This means that if we XOR x with the value (1u << 3)
, we're XOR-ing it with
0000000000001000
So this means "keep all the bits but the fourth bit set as is, but flip the fourth bit." More generally, if you want to flip some number of bits, XOR the value with a number that has zero where you want to keep the bits intact and one where you want to flip this bits.
Hope this helps!