Version: Flash 5+ This information originally appeared in chapter 5 of ActionScript: The Definitive Guide, First Edition. In the interest of space, it was removed from the second edition and is now available online only. In order to track and manipulate a series of options as efficiently as possible, we can use the bitwise operators. Technically, the bitwise operators are mathematical operators, but they're typically used in a logical, not mathematical, context. Bitwise operators can access the individual binary digits (bits) in an integer. To understand how this works, you need to know how numbers are represented in binary format. A binary number is stored as a sequence of ones and zeros that represent the number in the base-2 number system (i.e., the binary system). Each column in a number represents the base of the number system to some power. Binary uses the number 2 as its base, so the first four columns of a binary number represent, from right to left, the 1's column (20), the 2's column (21), the 4's column (22), and the 8's column (23). Here are some sample binary numbers with explanations of how their column values can be used to calculate their base-10 (decimal) equivalent:
In binary, the columns we've been discussing are referred to as bits (short for "binary digit"). A four-bit number, for example, is a number with four digits (each of which may contain a one or a zero). The rightmost bit is considered bit 0; the bit to its left is bit 1, and so on. Following is an 8-bit number with a 1 in bits 0, 6, and 7; the bits are labeled above the number:
As with all numbering systems, the largest value for a single digit is one less than the base (also known as the radix). For example, in base-10 (decimal), the largest single digit is 9. Notice that because we're using 2 as our base, each binary digit must be either a 0 or a 1. We know that the numbers 1 and 0 are equivalent to the Boolean values A bit that has the value 1 is said to be set (i.e., on or Bitwise programming nearly always involves situations in which a series of properties can be turned on or off. Using bitwise operators, we can concisely represent many options within a single numeric value instead of using multiple variables. This provides better performance and lower memory consumption. Suppose we're building a Flash site that sells cars. For the sake of simplicity, let's say there's only one kind of car for sale, but users can customize their car with any combination of four options: air-conditioning, a CD player, a sunroof, and leather seats. It's the job of our Flash program to come up with a total price for the car including all the options, and it's the job of a server-side program to track that information as part of the user's profile. We could store the car's options with four separate Boolean variables, like this:
Essentially, we've got four switches-one for each optional component of the car-each requiring a variable. That works fine, but it means we need four variables in memory and four fields in the user-profile database on the server. If we instead record the car's options as individual binary digits, we can store all four options in a single 4-bit number: air-conditioning is bit 0 (the 1's column), the CD player is bit 1 (the 2's column), the sunroof is bit 2 (the 4's column), and the leather seats are bit 3 (the 8's column). Here are some sample configurations that show how a single number can represent any combination of the four options:
Whenever we want to add or remove options, we just add or subtract the value of the appropriate bit:
So now we know how to store multiple options as a series of bits in a single number. How do we examine those bits to calculate the cost of the car? We need to use the bitwise operators. We'll run through the operators first and come back to the car example after we're done. Bitwise ANDThe bitwise AND operator (
The operands of bitwise AND can be any numbers, but they are converted to 32-bit binary integers before the operation occurs. If an operand has a fractional value such as 2.5, the fraction is discarded. Note that the bitwise AND uses the single-character operator, Bitwise AND returns a number whose value is determined by comparing the individual bits in the numeric operands, Bitwise AND operations are most easily pictured by arranging the binary equivalents of the decimal operands vertically and lining up their bit columns. In this format, it is easy to tell which bits of the operands both contain 1s. In this example, bit 2 (the third bit from the right) is 1 in both operands and is therefore set to 1 in the result. Other bits are set to 0 in the result:
In this example, bits 0 and 3 are 1 in both operands and are therefore set to 1 in the result. Bits 1 and 2 are set to 0 in the result:
ActionScript uses decimal (base-10) numbers instead of binary numbers, which makes it harder to visualize bitwise operations. Here is what the previous operations look like in actual code where decimal numbers are used:
In practice, the bitwise AND operator is used to check whether a particular flag or set of flags (i.e., bits) is The following example checks whether bit 2 (which has the value 4) is set to
Or, we can check whether either bit 2 or bit 3 (which has the value 8) is set to
Note that the preceding example checks whether bit 2 or bit 3 is set using the
The bitwise AND operator is also used to set individual bits in a number to The Bitwise OR operator (
The operands can be any numbers, but they are converted to 32-bit binary integers before the operation occurs. The fractional portion of an operand, if any, is discarded. Note that the bitwise OR uses the single-character operator, Each bit in the result is determined by taking the logical OR of the bits of the two operands. Therefore, if a bit is set to 1 in either (or both) operand1 or operand2, that bit will be set to 1 in the result. Compare the following pseudoexamples to those shown earlier for the bitwise AND operator. In this example, only bit 1 is set to 0 in the result because bit 1 is 0 in both operands. The other bits are set to 1:
In this example, all bits are set to 1 in the result because each bit contains a 1 in at least one of the two operands:
In actual code, where decimal numbers are used, this reads:
In practice, we often use bitwise OR to combine multiple numbers that represent individual options into a single numeric value that represents all the options of a system. For example, the following code combines bit 2 (value 4) and bit 3 (value 8):
The bitwise OR operator is also used to set an option to true in an existing value. The following example sets the option represented by bit 3 (value 8) to true. If the value in bit 3 is already true, it is untouched:
Multiple bits can also be set at once:
Bitwise XOR
We're officially getting into weird punctuation symbols for our operators. The bitwise XOR (eXclusive OR) operator is the caret symbol,
The operands can be any numbers, but they are converted to 32-bit binary integers before the operation occurs. The fractional portion of an operand, if any, is discarded. The bitwise XOR operator differs from the bitwise OR operator in that the result contains a 0, not a 1, for any bit containing a 1 in both its operands. In other words, the XOR result contains a 0 for any bits that are the same in both operands and contains a 1 for any bits that differ between the two operands. In this example, bits 0 and 3 match in both operands, so those bits are set to 0 in the result. Bits 1 and 2 differ in the two operands, so they are set to 1 in the result:
In this example, all the bits match in both operands, so the result is all zeros:
In this example, bits 0, 2, and 3 differ in the two operands, so those bits are set to 1 in the result. Bit 1 is the same in both operands, so it is set to 0 in the result:
Translated to decimal numbers, the preceding examples become:
The bitwise XOR operator is typically used to toggle options between 1 and 0 (
Bitwise NOT
Unlike bitwise AND, OR, and XOR, which all produce a number resulting from two other numbers, bitwise NOT changes the bits of a single number. It uses the tilde symbol (
The operand can be any number, but it is converted to a 32-bit binary integer before the operation occurs. Any fractional portion of the operand is discarded. Bitwise NOT simply inverts the bits in its operand. For example:
which, in decimal, read:
It's impractical to go into a lesson on negative binary-number representation systems here, but advanced programmers should note that bitwise operations represent negative binary integers using the twos-complement system. To those unfamiliar with this notation, simply remember that the return value of a bitwise NOT operation is one less than the value obtained by taking the negative of the original operand. For example:
The bitwise NOT operator is typically used with the bitwise AND operator to clear specific bits (i.e., set them to 0). For example, to clear bit 2, we could use:
The expression
The same technique can be used to clear multiple bits at once; the following example clears bits 2 and 3:
The Bitwise Shift Operators
As we've seen, bitwise programming treats binary numbers as a series of switches. It's frequently useful to move those switches around. For example, if we have bit 0 on, and we decide we want to turn it off and turn bit 2 on, we could simply move bit 0 left two places. Or if we want to know whether bit 5 of a number is on, we could move that bit right five places and then check bit 0's value. The bitwise shift operators let us perform such movements. Bitwise shift operators also allow us to rapidly multiply and divide by multiples of 2. If you wanted to divide a decimal (base-10) number by 10, you could simply shift the decimal point one place to the left. Likewise, to multiply by 10, you simply shift the decimal point one place to the right, and to multiply by 103 (i.e., 1000) you would shift the decimal point three places to the right. The bitwise shift operators let us perform an analogous operation with binary numbers. Shifting bits to the right divides a number by 2 for each position shifted. Shifting bits to the left multiplies a number by 2 for each position shifted. Signed right shiftThe signed right shift operator can be used to divide a number by some power of 2. It uses the
where All bits are shifted right by the number of positions specified by
Shifting a number right one bit is like dividing it by 21 (i.e., 2). In decimal this reads:
Note that any remainder is discarded:
For negative numbers, just as for positive numbers,
Unsigned right shift
The unsigned right shift operator, created using three successive greater-than signs (
It works like the signed right shift operator except that bits vacated by the shift are always filled with zeros (regardless of whether The left shift operator can be used to multiply a number by some power of 2. It uses the
where All bits are shifted left by the number of positions specified by
Shifting a number left by 4 bits is equivalent to multiplying it by 24 (i.e., 16). In decimal, this reads:
Notice that in prior examples, we "manually" specified the value associated with a particular bit: 1 for bit 0, 2 for bit 1, 4 for bit 2, 8 for bit 3, and so on. The left shift operator is very handy for calculating a bit position's equivalent value:
The left shift operator is also handy for dynamically selecting bits by numeric index rather than bit value. This example counts up all the bits set to one in a number.
The next example is a variation on the previous example using the right shift operator. We can repeatedly right-shift the value and check its rightmost bit (bit 0), instead of using the left shift operator to calculate the bit value associated with each bit.
The variable We began our look at bitwise operators using the example of a Flash site that sells cars. Now that we've seen how bitwise operators work, let's use them to determine the cost of a car, as shown the next example. You can download the .fla file for this example here.
To avoid hardcoded bit values throughout your code, it's good practice to store the bit values corresponding to specific options in variables, such as:
Reader Exercise: Rewrite the car example using variables and the left shift operator instead of hardcoded bit values to represent the options. Why bitwise?Although our car example would be easier to understand as a series of Boolean operations, bitwise operations are extremely fast and compact. Anytime we can speak to a computer in its native binary tongue, we save room and gain speed. For the sake of comparison, consider a situation in which we're tracking a user's profile, and each user has 32 settings that can be on or off. In a normal database, we'd need 32 fields for each user. If we have a million users, that's a million copies of 32 fields. But when we use bitwise programming we can store the 32 settings in a single number, requiring only one field in the database for each user! Not only does this save disk space, but every time we access a user's profile, we need transfer only a single integer, not 32 Boolean values. If we are processing millions of transactions, saving a few milliseconds per transaction can measurably improve system performance. For further study, see Gene Myers' excellent article for C programmers, Becoming Bit Wise, which was taken down from cscene, but is archived at http://www.elitecoders.de/mags/cscene/CS9/CS9-02.html. (Note: that link may be volatile. Search on google if necessary.) |