We – Flash Engineers – rarely use these operators, but when we do, performance and efficiency are in mind. Bitwise operators are used to manipulate bits of data/variables, they operate on single or sets/pairs of bits – otherwise known as bit patterns – at the level of their individual bits.
There are two types of bitwise operations; bitwise operators and bitwise shifts.
Bitwise Operators consist of the NOT, OR, XOR and AND. Bitwise Shifts consist of the Arithmetic, Logical, Rotary No Carry and Rotary Carry Through. One note on bitwise shifts is that they are sometimes considered bitwise operations, because they operate on the binary representation of its numberical value, but the bitwise shifts do not operate on pairs like the bitwise operators, and therefore cannot be considered bitwise operations, but bitwise nonetheless.
As indicated earlier, bitwise operators manipulate bits of unsigned integers. Let’s look at some examples, by the way, put on your binary goggles.
So let’s say we have these constant variables.
private const A:uint = 1; //00000001 private const B:uint = 2; //00000010 private const C:uint = 4; //00000100 private const D:uint = 8; //00001000 private const E:uint = 16;//00010000 private const F:uint = 32;//00100000
NOT: Bitwise NOT(~), or the compliment operator, is a unary operation that performs a logical negation on each individual bit in a bit pattern, so, 0’s become 1’s and vise-versa. This operator will convert the number into a signed 32-bit integer and then performs a bitwise One’s Complement; and when converting unsigned numbers the Two’s Complement is performed.
//Example 1 trace(~A); //outputs -10 - decimal -2 //Example 2 trace(~-B); //outputs 01 - decimal 1
OR: Bitwise OR(|), takes 2 bit patterns of the same length and processes them by performing a logical inclusive OR on each pair. In each pair the result is 1 if the first or second or both bits are 1, otherwise double 0’s are 0.
//Example 1 trace(C | D); //outputs 00001100 - decimal 12 //Example 2 trace(E | F); //outputs 00110000 - decimal 48
XOR: Bitwise XOR(^), exclusive OR, performs a logical XOR on each pair of bits. The result in each position is 1 if the two bits are different and 0 if they’re the same.
//Example 1 trace(A ^ B); //outputs 00000011 - decimal 3 //Example 2 trace(C ^ D); //outputs 00001100 - decimal 12
AND: Bitwise AND(&), performs a logical AND on each pair of bits. In each pair, the result is 1 if both bits are 1, otherwise it’s 0.
//Example 1 trace(C & D); //outputs 00000000 - decimal 0 //Example 2 trace(E & F); //outputs 00000000 - decimal 0
For good reason, bitwise shifts are sometimes considered bitwise operations, since they operate on the binary level of a numeric/unsigned integer. However, bitwise shifts don’t operate on sets or pairs of bits, rather on entire register sets. In this type of operation, the register digits are moved or shifted to either left or right. So, these registers have a fixed number of slots, and when a set of digits are shifted either direction, we are left with the tail to deal with. There are four types of shifts, Arithmetic, Logical, Rotary No Carry and Rotary Carry Through. Bitwise shifts are another rare usage case in ActionScript 3, we can use these operation to manipulate graphics and color information.
Arithmetic Shift: in an arithmetic shift, the bits are discarded from either end they are shifted. For instance, in a left shift, zeros are filled in on the right, but in a right shift the sign bit is placed in the left or beginning of the register to preserve the sign or handle.
Logical Shift: a logical shift is very similar to the arithmetic shift in that when you shift the digits, zero’s are filled into the void. So they perform exactly the same operation, it just that the logical will set 0’s on either end of the digits instead of setting the sign bit to the left when performing a right shift.
Rotate No Carry: also known as the circular shift or bit rotation, shifts the bits as if the left and right ends of the registry were connected, sort of like a length modulated carousel.
Rotate Carry Through: is very similar to the Rotate No Carry shift where the two ends of the register and seperated by the carry flag or otherwise known as the C flag. The bit that is shifted in – on either end – becomes the old value of the C flag, and the bit that is shifted out becomes the new value of the C flag. We’ve all been through this is 2nd grad math and in accounting classes. Carry/borrow the number in additions and LIFO & FIFO.
AS3 Shifting: ActionScript, and other C Like languages and can perform bitwise shifts. The syntax is the same, with varied applications. In C and C++ the bitwise shift operators perform logical shifts on unsigned integers, in Java and AS3 they perform arithmetic and logical shifts on signed integers. The syntax for left and right shifts are “<<” and “>>” respectively, which perform arithmetic shifts. There’s also the “>>>” operator for logical shifts but since the logical and arithmetic left shifts are identical, there’s no need for a “<<<“, which doesn’t exist in the language.
var x:uint = 1, y:uint = 2, z:uint = 3; trace(x = y << z); //Shifts three bits to the left - outputs 16
Just like with operators there aren’t much applications for the shifters. However, if you’re looking for fast computations like for simulations or games, you might want to brush up on some of these bitwise techniques.
For instance, if you wanted to get the square root or multiply by any power of two to any number you might want to left shift like this:
var x:uint = 2; trace(x = x * 2); //Outputs 4 trace(x = x * 256); //Outputs 1024 //Same as x = 2; trace(x = x << 1); //Outputs 4 trace(x = x << 8); //Outputs 1024
Or if you wanted to divide by any power of two, you can do something like this.
var x:uint = 2; trace(x = x / 2); //Outputs 1 trace(x = x / 8); //Outputs 0.125 //Same as x = 2; trace(x = x >> 1); //Outputs 1 trace(x = x >> 8); //Outputs 0
Say you wanted to convert a Number/Float into a signed integer. You can do this:
var x:uint; trace(x = int(1.234)); //Outputs 1 //Same as trace(x = -1.234 >> 0); //Outputs -1
I’m sure we’ve all wanted to swap values, but had to use some sort of temporary variable to hold the value. We do this using the bitwise XOR assignment(^=).
var a:int = 5; var b:int = 10; var t:int = a; trace(a = b); //Outputs 10 trace(b = t); //Outputs 5 //Same as a = 5; b = 10; trace(a ^= b); //Outputs 15 trace(b ^= a); //Outputs 5 trace(a ^= b); //Outputs 10
Sometimes we need to convert negative number to positive and vise versa, this is also known as sign flipping. Get it? sign and unsign?
x *= -1; //Regular way. //Flip x = ~x + 1; //or x = (x ^ -1) + 1;
We’re constantly incrementing or decrementing values. Now this procedure is not necessarily faster nor does it require less typing, but it’s an option that’s on the table. As far as speed goes, pre vs. post. pre is slightly faster.
//increment/decrement x = -~x; // x++ x = ~-x; // x--
If you needed to extract color values from each component, you would do something like this.
//24bit var color:uint = 0x003366; var r:uint = color >> 16; //Outputs 0 var g:uint = color >> 8 & 0xFF;//Outputs 51 var b:uint = color & 0xFF; //Outputs 102
//32bit var color:uint = 0xFF003366; var a:uint = color >>> 24; //Outputs 255 var r:uint = color >>> 16 & 0xFF; //Outputs 0 var g:uint = color >>> 8 & 0xFF; //Outputs 51 var b:uint = color & 0xFF; //Outputs 102
We can reverse engineer the previous calculations and combine the color components.
//24bit var r:uint = 0x00; var g:uint = 0x33; var b:uint = 0x66; var color:uint = r << 16 | g << 8 | b; //Outputs 13158
//32bit var a:uint = 0xFF; var r:uint = 0x00; var g:uint = 0x33; var b:uint = 0x66; var color:uint = a << 24 | r << 16 | g << 8 | b; //Outputs 4278203238