In the last lesson, we learned about bits - the smallest units of binary code that our machines use to operate. In this lesson, we'll learn how to work with bits, including comparing and manipulating them.
Why learn about bit manipulation? While working with binary code might be necessary on the lowest level of the machine, why would we use bits in higher-level languages? Well, it turns out that bits are very useful for a number of different things:
The AND bitwise operator (
&) is used to check if both binary values are true. Remember that 1 corresponds to true, and 0 corresponds to false.
The following corresponds to 1 because both values are 1.
1 & 1 1
In other words,
1 & 1 returns
By the way, don't try any of this in the console just yet as there's a big gotcha that we'll cover in just a moment.
Meanwhile, all of the following will return 0:
1 0 0 & 0 & 0 & 1 0 0 0
This is because both values need to be 1 for it to return 1.
We can apply this concept for longer binary numbers. Each column will be checked to see if it should return 0 or 1.
100110 & 110011 100010
In the first column, when we compare the numbers in the first two rows, they are both 1. So the result is a 1. For the next column, we have a 0 and 1, so the result is a 0. We check the value of each column to get a final result of
Now for the console gotcha. What happens if we try this in the console?
// Won't work correctly. > 100110 & 110011 99594
0b like this:
// Still won't work correctly. > 0b100110 & 0b110011 34
Number.prototype.toString(2) (which we learned about in the last lesson) to translate it right back into its binary equivalent. This is how we need to get the result we want in binary:
// This works! > (0b100110 & 0b110011).toString(2) "100010"
The OR bitwise operator (
|) will return a 1 if any of the values in a column are 1. It will return a 0 if all the values in a column are 0.
1000101 | 0111011 1111111
In the example above, at least one of the columns always has a 1 value so the result of each column is 1.
The XOR operator (
^), short for the exclusive or operator, checks to see if the bits at the same position of multiple binary numbers are the same or different. If they are different, it returns 1. If they are the same, it returns 0.
01001100 ^ 10101010 11100110
The NOT operator (
~) switches every bit in a binary number to its opposite. For example, the following:
All of the zeroes become ones and all of the ones become zeroes. In addition to that, the number also becomes negative!
How does a computer know if a binary number should be negative or not? Well, there's an additional bit that is reserved for if a number is positive or negative. If that reserved bit is a 1, then the number is negative. However, when we use binary numbers, we will use a negative sign for negative binary numbers because it is challenging to depict a reserved 0 or 1 that shows whether the number is positive or negative. Because we aren't transistors, there is no need for us to use a special bit.
There are also three bitwise shift operators which will shift bits to the left or right.
<< (left shift) operator shifts bits to the left, filling in the empty space on the right-hand side with zeroes.
For instance, if we do a left shift of 2 spaces of the following binary number:
We get the following result:
We've just shifted everything over to the left by adding two zeroes at the end.
We can also shift values to the right. This won't add zeroes, though - instead, it will drop the specified number of bits from the end of the binary number. The sign-propagating part sounds fancy but it just means that the shift won't change the sign of the binary number (whether it's positive or negative).
Here's an example. The following:
The last three digits on the right are removed.
All of the previous operators we've covered are also in Ruby and C#. This one, however, is not, most likely because it doesn't have many use cases.
Like the last shift, the zero-fill right shift shifts bits a specified number. The difference is that the sign is shifted to zero as well - which means the number will always be positive after a zero-fill right shift. If the number is already positive, a zero-fill right shift and a sign-propagating right shift operate in exactly the same way.
0b which we need to use to specify that a number should be evaluated as binary, not decimal. In addition to that, we need to use the method
In the next lesson, we'll have an opportunity to practice solving some problems using bitwise manipulation!
Lesson 3 of 7
Last updated January 20, 2021