(Note: All example code will be in Java, but it's pretty easy to understand, so if you know any C language you should get it)
First things first, lets list what all of the operators are:
- Right Shift
- Shown with a '>>', and moves all bits to the right equal to the number specified on the right of the operator.
- Left Shift
- Shown with a '<<', and moves all bits to the left equal to the number specified on the right of the operator.
- OR
- Shown with a '|', also known as the Inclusive OR operator.
- XOR
- Shown with a '^', and also know as the Exclusive OR operator.
- AND
- Shown with a '&', similar to '&&' binary operator.
- Unary Bitwise Complement
- Shown with a '~', similar to the unary '+' or '-' operators for integer variables.
So there. Now we know what they are, and you might have some idea on what they actually do. But you might not also, so let's go a little in depth.
Bitwise Operators Explained
Bitwise Operators Explained
Right Shift
The right shift is relatively simple. Let's say you have the following code:
class BitwisePractice{ public static void main(String[] args){ int num1 = 0b101; //Which is equal to 5 System.out.println(Integer.toBinaryString(num1 >> 1)); } } --Prints-- 10 //Which is equal to 2
Left Shift
Left Shift is very similar to Right Shift, but now it has a somewhat different application as opposed to division. Let's see an example first, then I'll explain:
class BitwisePractice{ public static void main(String[] args){ int num1 = 0b101; //Which is equal to 5 System.out.println(Integer.toBinaryString(num1 << 2)); } } --Prints-- 10100 //Which is equal to 20
OR
Now we start getting to the more interesting operators. The OR operator compares every bit between two variables, and for every point that is either '1' and '0', or '1' and '1', evaluates to '1' on the result of the equation, for example:
class BitwisePractice{ public static void main(String[] args){ int num1 = 0b10100010; //Which is equal to 162 int num2 = 0b00100100; //Which is equal to 36 //10100110 (This is what should be printed) System.out.println(Integer.toBinaryString(num1 | num2)); } } --Prints-- 10100110 //Which is equal to 166
XOR
XOR is very similar to OR. It is known primarily as the Exclusive OR operator, and that is because it functions the same as the OR operator, except that any position that on both inputs has a '1' results in a '0' in the print statement. I will not further explain this as I feel it is simply illustrated, but here is an example using the numbers from the OR example:
class BitwisePractice{ public static void main(String[] args){ int num1 = 0b10100010; //Which is equal to 162 int num2 = 0b00100100; //Which is equal to 36 //10000110 (This is what should be printed) System.out.println(Integer.toBinaryString(num1 ^ num2)); } } --Prints-- 10000110 //Which is equal to 134
AND
The AND is used a lot (In Bitwise terms), and is like the opposite of the OR operator. AND takes two variables, compares them, and if two positions both share a '1', then that is copied down into the resulting equation, for example:
class BitwisePractice{ public static void main(String[] args){ int num1 = 0b1010; //Which is equal to 10 int num2 = 0b0110; //Which is equal to 6 //0010 (This is what should be printed) System.out.println(Integer.toBinaryString(num1 & num2)); } } --Prints-- 0010 //Which is equal to 2
Unary Bitwise Complement
The Bitwise Complement operator is also fairly simple. It is applied to a single variable, similar to a negative symbol (-). When it is applied, it shifts all the bits to their opposites, all '0's to '1's, and all '1's to '0's. This is the same as making the number negative, then subtracting 1, which can easily be expressed in the equation, '(n * -1) - 1'. Here is some example code:
class BitwisePractice{ public static void main(String[] args){ int num1 = 0b0001; //Which is equal to 1 System.out.println(Integer.toBinaryString(~num1)); } } --Prints-- 11111111111111111111111111111110 //Which is equal to -2
An Example of Usage
The applications of Bitwise operators are infinite, like writing checksums, compressing files, and so on, but let's start small.
The House with 8 Doors
The environment we are going to now mirror is a House. With Eight Doors. It is our job to know which doors are open, closed, and be able to close or open any door.
So let's start small.
class House { public static byte doorsOpen = 0b00000000; }
Opening the Door
Now we want to write a method that accepts an int that represents which door we are trying to open. So let's do this step by step. Here is my method declaration:
public static void openDoor(int doorNum){ }
byte mask = (byte)(1 << doorNum);
Well I hope you figured it out by now, but if you haven't, it's with an OR operator. So all you have to do is add the following:
doorsOpen = (byte)(doorsOpen | mask);
(Note: In Java you HAVE to typecast the result as a byte, because it might read it as an int, and give you an error)
Checking the Door
The next step is to create a method that returns either 'true', or 'false', depending on if the specified door is opened, or closed. I've set my method up like so:
public static boolean isDoorOpen(int doorNum){ }
The next step is to create an 'if' statement that checks if 'doorsOpen' is greater than 1 after being applied to the mask. Before you read on try to accomplish this.
The easiest way to do this is like so:
return (byte)(doorsOpen & mask) > 0;
Do it yourself
The next step is to implement a method that allows you to switch the doors state. This just means that if the door is closed, open it, and if it is opened, close it. Note that closing a door is a bit more difficult, but if you can figure it out, more power to you!
Check the solution below:
Closing the Door
The next step is probably the most difficult of all of the methods yet. Because it is. Sorry. We still create our mask like normal, but this time when we are reassigning 'doorsOpen', we have to use the 'Bitwise Complement' Operator, and a AND operator. We have to use them in conjunction because we want to only change the position specified when the method is called, and that's what this allows us to do. If we say 'doorsOpen & ~mask' then we will only be resetting the position specified, as the AND operator only let's positions stay the same if they are the same, and this also safeguards from just switching states instead of closing the door. I hope you understand. Here is the example code:
public static void closeDoor(int doorNum){ byte mask = (byte)(1 << doorNum); doorsOpen = (byte)(doorsOpen & ~mask); }
Homework?
Using the XOR operator it is possible to switch the values of two different variables. Can you figure out to do this? If so post it in the comments! And go ahead comment if there was anything wrong with all of this!
Finished? Go ahead an read my Programmer Bill of Rights for a good laugh.
The XOR swap is nice trick but isn't too useful anymore. But it might come handy if you do assembly programming and just want to swap values of two registers without involving a third one as temporary storage.
ReplyDeleteThe swap (in C):
a ^= b
b ^= a
a ^= b
It might be worth mentioning the difference between sign-extending and logical shifts : the difference between (>>) on signed vs unsigned operands in C, and the difference between (>>>) and (>>) in Java.
ReplyDeleteThere is nothing wrong with this statement:
ReplyDeleteif((byte)(doorsOpen & mask) > 0)
return true;
return false;
but it is unnecessary. (byte)(doorsOpen & mask) > 0 already returns a boolean value you can just return that. It will be true if the bit is set to one, and false otherwise.
This is probably formatted wrong, since the comments don't seem to support HTML.
Yeah, I'll change that. I guess I let it slip because I was so excited about... bits? Haha, thanks.
DeleteThis comment has been removed by a blog administrator.
ReplyDelete