For purposes of this article, it is going to be assumed that you are already cognizant of Perl syntax and have a cogent command of the basic arithmetic and logical operators. It is also going to be implied that you know what a bit is and how data is represented as a binary string. Ergo, this article is not going to elucidate any of those rudimentary principles. The intent of this article is to imbue upon you the workings of the bitwise operations available to you in Perl.
The bitwise operators were fabricated so that developers would have a means of working with the binary representations of data. Most often, the use of bitwise operators is found in low-level functions (e.g., when directly accessing the hard disk or when working with socket functions) and when implementing some function(s) salient to cryptology. There are six bitwise operators available to you in Perl. The constituents of these bitwise operations include the AND, OR, XOR (exclusive-or), complement, shift left, and shift right operators. To whet your appetite, I will concisely define how each of these operators function and then expand on this information at a later juncture in the article.
The AND operator, represented by the & (ampersand) sign, compares two bits and generates a result based on that comparison. If both bits are 1 then the result is 1. If one of those bits is 0 then the result is 0. Basically, if the first bit AND the second bit are 1 then the result is 1, otherwise the result is 0.
The OR operator, represented by the | (pipe) sign, compares two bits and generates a result based on that comparison. If either of the bits is 1 then the result is 1. Basically, if the first bit OR the second bit OR both bits is 1 then the result is 1. Otherwise, the result is 0. That is to say, if and only if both bits are 0 will the result will be 0.
The XOR (exclusive-or) operator, represented by the ^ (circumflex) sign, compares two bits and generates a result based on that comparison. The XOR operator is similar to the OR operator with one major difference. With the OR operator, either bit has to be 1 in order for the result to be 1. This means that apropos to the OR operator, if both bits are 1 then the result will still be 1. In contradistinction, the XOR operator works by saying that if the first bit OR the second bit is a 1 but NOT both bits, then the result is a 1. Otherwise, the result is a 0. Basically, if one of the bits is a 1 and the other bit is a 0, then the result is 1. I will explicate the workings of this operator in more depth shortly, for those of you who may find the preceding description somewhat nebulous.
The shift operators will be expounded upon after the previous operators have been shown in example merely because they are considered to be a little more abstruse and I can't give them justice in a few sentences. You really have to see them in action to understand them.
The COMPLEMENT operator, represented by the ~ (tilde) sign, can be somewhat confusing for people who are new to bitwise operations. There is also a lot to say about it. In fact, too much for a simple article like this one. Therefore, I am going to simply suggest you read about it here: http://en.wikipedia.org/wiki/Two%27s_complement. Although, I highly doubt that if you are new to bitwise operators you are going to have a need for it right now.
But, for now, let's get into some code. We'll start our code examples with the AND operator.
#!/usr/bin/perl -w
use strict;
my($a, $b, $c);
$a = 1;
$b = 2;
$c = $a & $b;
printf("The result is: %d\n", $c);
When you run this program, you will see that "The result is: 0" is output. To understand this, we have to think of the numbers 1 and 2 (the values of $a and $b, respectively) as they are represented in binary format. So we have 00000001 for 1 and 00000010 for 2. What Perl does is line them up just like we were going to do simple binary arithmetic, like so:
00000001
00000010
As explained earlier, the AND operator compares two bits and generates a result based on that comparison. As we know from the explanation above, both bits have to be 1 in order for the result to be 1. The bits should be visualized as being juxtaposed in columns instead of rows, as this is how Perl sees them. So, we are left with the following which is, noticeably, why we get 0 for our result:
00000001
00000010
--------
00000000
Now let's change the script just a bit by making both our $a and $b variables contain the same value:
#!/usr/bin/perl -w
use strict;
my($a, $b, $c);
$a = 42;
$b = 42;
$c = $a & $b;
printf("The result is: %d\n", $c);
When you run this program, you will see that "The result is: 42" is output. Again, thinking in terms of binary, the number 42 is represented as the string 00101010. So, the operation actually becomes:
00101010
00101010
--------
00101010
You may be thinking, "So, if you AND two like values together, you get that same value back?" Quite simply, yes. Try a few on your own for a little exercise. But what happens if you don't AND two similar values together or the values aren't single digit numbers? Well, let's see:
#!/usr/bin/perl -w
use strict;
my($a, $b, $c);
$a = 42;
$b = 26;
$c = $a & $b;
printf("The result is: %d\n", $c);
When you run this program, you will see that "The result is: 10" is output. Why? To answer that, we need to see what's going on in binary format:
42 = 00101010
26 = 00011010
--------
10 = 00001010
It's really self-explanatory. Hopefully you get the idea behind how the AND operator works. So now lets take a look at the OR operator with a code example. We're going to use the same program we used with the AND explanations, but with one little adjustment:
#!/usr/bin/perl -w
use strict;
my($a, $b, $c);
$a = 1;
$b = 2;
$c = $a | $b;
printf("The result is: %d\n", $c);
Notice that $c = $a | $b this time instead of $c = $a & $b. Here, we see that "The result is: 3" is output. Analyzing the binary we can see that (from the description above):
1 = 00000001
2 = 00000010
--------
3 = 00000011
Let's look at one more OR example, using 42 and 26 as values again:
#!/usr/bin/perl -w
use strict;
my($a, $b, $c);
$a = 42;
$b = 26;
$c = $a | $b;
printf("The result is: %d\n", $c);
Here we see that "The result is: 58" is output. Let's see why:
42 = 00101010
26 = 00011010
--------
58 = 00111010
Once you understand the AND operator, understanding how OR works is a cinch. Moving onward to examine XOR, we have another code example:
#!/usr/bin/perl -w
use strict;
my($a, $b, $c);
$a = 42;
$b = 26;
$c = $a ^ $b;
printf("The result is: %d\n", $c);
Once we run this program, we notice the result is 48. In binary:
42 = 00101010
26 = 00011010
--------
48 = 00110000
Refer to the explanation above and you will see why this works. It's actually just as easy to understand (once you see XOR in operation) as the AND and OR operators. Further explanation isn't really needed but I encourage you to play around and see what different results you get when using letters instead of numbers and so on. Also, for a somewhat interesting topic, do some research on XOR encryption.
Now, we're going to discuss the shift left and shift right operators. Basically, you are doing just what the operations say you will be doing; shifting the bits to the left in the case of the shift left operator (<<) or shifting the bits to the right in the case of the shift right operator (>>). The left operand specifies the value to be shifted. The right operand specifies the number of positions that the bits in the value are to be shifted. But let's dig into some code here for a moment so we can dissect it and get a better feel for what we are dealing with:
#!/usr/bin/perl -w
use strict;
my($a, $b);
$a = 128;
$b = $a >> 2;
printf("The result of %d >> 2 is: %d\n", $a, $b);
When we run this program we get "The result of 128 >> 2 is: 32". We know that the number 128 in binary is 10000000. We are telling Perl to shift that value to the right by 2 positions. What we end up with is 00100000, which is the number 32. The process works in reverse for the shift left operator:
#!/usr/bin/perl -w
use strict;
my($a, $b);
$a = 32;
$b = $a << 2;
printf("The result of %d << 2 is: %d\n", $a, $b);
When we execute this program we get "The result of 32 << 2 is: 128". All we are doing here is taking 32 (00100000) and shifting it 2 places to the left giving us 128 (10000000). Actually, these two operators aren't as abstruse (at least to me) as the description above may make them sound, but some people for whatever reason have to see these operators in action before they really understand what is happening. Now, you may be wondering when you'll ever have to shift bits around like this. The main reason people use shift left and shift right are for optimization purposes (especially when optimizing algorithms). For example, when you need to multiply or divide, using shifts produces results a lot faster than using the * or / operators. The technical explanation behind this is beyond the scope of this article, but if you're interested, Google will be able to help you with your research. Also, most introductory level books on algorithm design and/or theory provide good insight into this.
This concludes my article. Hope you learned something!