Bitwise operators are one of those concepts that a lot of programmers don’t understand. These are not used a great deal anymore so you can get away with not knowing them but they can still come in handy for a number of different scenarios.
If you end up writing algorithms for encryption and video compression then it is definitely something you need to know.
As you can probably guess from the name, bitwise operators work on the individual bits. If you want to perform operations on binary numbers then you need to use bitwise operators.
Bitwise operations aren’t the most intuitive of things so before I go through the different types of bitwise operators it is worth looking at why we use them.
Bitwise operators were used a lot more in programming when computers didn’t have as much memory in them as they do now. Bitwise operators are still used for those working on embedded devices that have memory limitations.
The smallest amount of space a variable can take up is 1 byte. The reason for this is that it is the smallest unit of addressable space that a CPU can reference.
Even a boolean that only has the values
false and therefore should only take up 1 bit still uses a full byte and has the remaining 7 bits padded out.
0000 0001 = true 0000 0000 = false
This isn’t great if you are trying to save memory.
Most applications have multiple flags, especially those that are referencing physical hardware.
To make the most out of the available space programmers would combine multiple flags into a single byte allowing them to store up to 8 different boolean values.
Which is just what you need if you are trying to conserve memory on a small device.
The one place you might have seen multiple flags being stored is with unix file permissions. Typically you might see file permissions denoted as numbers like
The first number is the permissions for the file owner, the second is the permissions for the group and the third is for everyone else.
The number tells you whether they have permission to read a file, write to a file or execute a file.
To understand what each number means you need to convert the number into binary first.
7 in binary is
111 which means they have the ability to read, write and execute.
6 in binary is
110 which means they have the ability to read and write but not execute.
4 in binary is
100 so they only have permission to read the file.
Now we have an example in mind let’s have a look at what the bitwise operators are.
There are 6 bitwise operators and each has a different purpose.
In most programming languages we use a single ampersand (&) for the AND operator.
The best way to describe how the operators work is to use truth tables.
Using the AND operator with 2 numbers will leave you with the bits that match each number.
For example, if we take 7 and 6:
7 & 6 = 6 --------- 1 1 = 1 1 1 = 1 1 0 = 0
Here we can see that the first 2 rows have a matching bit but the last one doesn’t so the final result is
6 as a decimal.
Going back to our file permissions example. The AND operator can be used to test whether the user has permission to access something.
As the AND operator will give you only the bits back that match you can test to see if your user has those permissions.
READ_PERMISSION = 4 WRITE_PERMISSION = 2 EXECUTE_PERMISSION = 1 userPermissions = 6 if (userPermissions & READ_PERMISSION) == READ_PERMISSION: print("Can Read") else: print("Cannot Read") print(userPermissions & READ_PERMISSION)
Can Read 4 0b100
The OR operator compares each bit and will give you the result if either one or the other is set to 1.
If we look at 5 and 6 using the OR operator and compare each bit we can see at least one of the rows the bit is set. The final result is therefore going to be 111 in binary or 7 in decimal.
5 | 6 = 7 --------- 1 1 = 1 0 1 = 1 1 0 = 1
You can use the OR operator to combine multiple flags together into a final result.
Here we put together the read-and-write permission using the OR operator and the final result we can see is 6.
READ_PERMISSION = 4 WRITE_PERMISSION = 2 EXECUTE_PERMISSION = 1 userPermissions = READ_PERMISSION | WRITE_PERMISSION print(userPermissions) print(bin(userPermissions))
You can even use the OR operator when assigning a value to an existing value.
Say we want to give the user the execute permission again we can add that using the OR assignment operator
READ_PERMISSION = 4 WRITE_PERMISSION = 2 EXECUTE_PERMISSION = 1 userPermissions = READ_PERMISSION | WRITE_PERMISSION userPermissions |= EXECUTE_PERMISSION print(userPermissions) print(bin(userPermissions))
This operator requires each bit to be the opposite of the other. When one is zero the other one has to be one.
If we look at the values 5 and 6 again. For the first bit, they both have the value 1 and therefore the exclusive or will return zero. The last 2 bits are both opposites and so that will return one. The final result is 011 which is 3 in decimal.
5 ^ 6 = 3 --------- 1 1 = 0 0 1 = 1 1 0 = 1
If we go back to our permissions example you can use the exclusive or remove permissions from the existing permission set.
Here we have a user who has the permissions to read and write. With the exclusive or assignment, we can remove the write permission from the user.
READ_PERMISSION = 4 WRITE_PERMISSION = 2 EXECUTE_PERMISSION = 1 userPermissions = READ_PERMISSION | WRITE_PERMISSION userPermissions ^= WRITE_PERMISSION print(userPermissions) print(bin(userPermissions))
The NOT operator
~ will give you the opposite value for each of the bits in your number.
Unlike the other operators, it doesn’t work on two values but just flips the value of each of the bits in the number.
~5 1 = 0 0 = 1 1 = 0
If you do this example in python with a number you are going to get a negative number which might be a bit surprising at first.
As I mentioned in my last video on binary numbers the first bit of a number denotes the sign of the number with 0 being positive and 1 being negative.
When you use the NOT operator it flips all the bits in the number so the first bit becomes 1, therefore, making it negative.
As Python uses an 8-bit number to represent small numbers, 5 would be written like this.
When this is reversed it becomes:
Binary numbers use something called the two’s complement representation. All you need to understand is that in this case, an 8-bit negative number will start from
We can then add each bit to this which gives us the final result of
-128 + 2^6 + 2^5 + 2^4 + 2^3 + 2^1
As the name suggests these operators shift all of the bits either to the left or to the right.
Again like the NOT operator this only works on a single number and the second number denotes the number of bits to shift by
The number 5 in a 8-bit binary is
If we use the left shift operator with a 2, all of the bits are going to be shifted two bits to the left.
number = 5 print(number) print(bin(number)) number = number << 2 print(number) print(bin(number))
5 0b101 20 0b10100
The 2 leftmost bits are going to be discarded, although these are 0 in our case anyway. The other bits are going to shift left by 2. So this then becomes
If we convert it to decimal is going to give us 20.
If we then take this number and shift it right by 2 bits then it is going to become 5 again.
number = 5 print(number) print(bin(number)) number = number << 2 print(number) print(bin(number)) number = number >> 2 print(number) print(bin(number))
5 0b101 20 0b10100 5 0b101
And that’s all there is to it. Bitwise operators can seem confusing at first but once you get used to thinking in binary they aren’t too scary.