Sunday, March 23, 2014

How to program with Bitwise Operators: The House with 8 Doors.

    Now I've been wondering for quite a while what Bitwise operators do. Sure, I understood what their functions are, but I couldn't think of a single way to use them! They just never seemed applicable XOR useful! So I finally got down and researched it, and I figured since there is a somewhat limited resource on how and when to use Bitwise operators, I'd publish my findings here, so here goes nothing.

(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

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
The Right Shift literally shifts all the bits one to the right. But another interesting thing is, for every bit you shift to the right is basically the same as dividing it by '2'. In my example I shifted by one, in integer division it'd look like this '5 / 2 = 2'. And if we continued this with larger numbers, we could get things like '200 >> 4 = 12'.

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
If you haven't already noticed, shifting to the left is the same as multiplying the number by '2' to the power of the amount of shifts. In this example we could right it as 'num1 * (2 * 2)', and for every additional shift, we add another ' * 2' in the parentheses.

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
Hopefully you can see that this is fairly simple, and the result is that every position that had a '1' in it somewhere resulted in a '1' in the print statement.

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
Notice that since an 'int' variable in Java is 32 bits, it prints all bits. This obviously changes from language to language.

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;
}
Since our house just happens to have 8 doors, it can be perfectly mirrored with Java's 'byte' variable, which only has 8 bits. Each bit will be tell us which Door is open. If a bit is on, it is open, whereas if it is off, it is closed. For this example we will not be tracking which door is which, but if you wanted you could definitely create an 'Enum' or some 'static int's to represent each door.

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){

}
The next step is to create a mask. Basically, a mask is a variable that allows us to get a single point from 'byte doorsOpen'. To create the mask we will create a 'byte mask', then assign it '1 << position'. This will create an empty 8 bit variable with a '1' at the index of the door, so then we can manipulate that single variable! The coding should look like so:
byte mask = (byte)(1 << doorNum);
Before you read on, guess how we will change the variable 'doorsOpen' to have a '1' at the masked point.

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){

}
Now go ahead and create a mask variable, just like we did before.
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.

5 comments:

  1. 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.

    The swap (in C):

    a ^= b
    b ^= a
    a ^= b

    ReplyDelete
  2. 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.

    ReplyDelete
  3. There is nothing wrong with this statement:

    if((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.

    ReplyDelete
    Replies
    1. Yeah, I'll change that. I guess I let it slip because I was so excited about... bits? Haha, thanks.

      Delete
  4. This comment has been removed by a blog administrator.

    ReplyDelete