Bitwise Shift Left/Right Assignment Definition

Bitwise operators are those strange looking operators that may look hard to understand... but not any more! This easy to follow article will help you understand what they are and how to use them, with a couple of practical examples as well to show you when and why you'd need them.


Introduction

Bitwise operators are operators (just like +, *, &&, etc.) that operate on and at the binary level. This means they look directly at the binary digits or bits of an integer. This all sounds scary, but in truth bitwise operators are quite easy to use and also quite useful!

It is important, though, that you have an understanding of binary numbers and hexadecimal numbers. If you don't, please check out this article - it will really help you! Below is a little application that will let you try out the different bitwise operators.

Don't worry if you don't understand what is going on yet, it will all be clear soon...


Recognizing the Bitwise Operators

Let's take a look at the bitwise operators that AS3 supplies. Many other languages are quite similar (for example, JavaScript and Java have practically identical operators):

  • & (bitwise AND)
  • | (bitwise OR)
  • ~ (bitwise NOT)
  • ^ (bitwise XOR)
  • << (bitwise left shift)
  • >> (bitwise right shift)
  • >>> (bitwise unsigned right shift)
  • &= (bitwise AND assignment)
  • |= (bitwise OR assignment)
  • ^= (bitwise XOR assignment)
  • <<= (bitwise left shift and assignment)
  • >>= (bitwise right shift and assignment)
  • >>>= (bitwise unsigned right shift and assignment)

There are a couple of things you should take from this: First, some bitwise operators look similar to operators you've used before (& vs. &&, | vs. ||). This is because they are somewhat similar.

Second, most bitwise operators come with a compound assignment form of themselves. This is the same as how you can use + and +=, - and -=, etc.


The & Operator

Up first: the bitwise AND operator, &. A quick heads-up though: normally, and take up 4 bytes or 32 bits of space. This means each or is stored as 32 binary digits. For the sake of this tutorial, we'll pretend sometimes that and only take up 1 byte and only have 8 binary digits.

The & operator compares each binary digit of two integers and returns a new integer, with a 1 wherever both numbers had a 1 and a 0 anywhere else. A diagram is worth a thousand words, so here's one to clear things up. It represents doing , which equals .

Notice how each binary digit of 37 and 23 are compared, and the result has a 1 wherever both 37 and 23 had a 1, and the result has a 0 otherwise.

A common way of thinking about binary digits is as or . That is, 1 is equivalent to and 0 is equivalent to . This makes the & operator make more sense.

When we compare two booleans, we normally do . That expression is only true if both and are true. In the same way, is equivalent, as the & operator only outputs a 1 when both binary digits of our two integers are 1.

Here's a table that represents that idea:

A neat little use of the & operator is to check whether a number is even or odd. For integers we can simply check the rightmost bit (also called the least significant bit) to determine if the integer is odd or even. This is because when converting to base 10, the rightmost bit represents 20 or 1. When the rightmost bit is 1, we know that our number is odd since we're adding 1 to a bunch of powers of two which will always be even. When the rightmost bit is 0, we know our number will be even, since it simply consists of adding up a bunch of even numbers.

Here's an example:

var randInt:int = int(Math.random()*1000); if(randInt & 1) { trace("Odd number."); } else { trace("Even number."); }

On my computer, this method was about 66% faster than using to check for even and odd numbers. That's quite a performance boost!


The | Operator

Up next is the bitwise OR operator, |. As you may have guessed, the | operator is to the || operator as the & operator is to the && operator. The | operator compares each binary digit across two integers and gives back a 1 if either of them are 1. Again, this is similar to the || operator with booleans.

Let's take a look at the same example as before, except now using the | operator instead of the & operator. We're now doing which equals 55:


Flags: A Use of the & and | Operators

We can take advantage of the & and | operators to allow us to pass multiple options to a function in a single .

Let's take a look at a possible situation. We're building a pop-up window class. At the bottom of it, we can have a Yes, No, Okay, or Cancel button or any combination of those - how should we do this? Here's the hard way:

public class PopupWindow extends Sprite { // Variables, Constructor, etc... public static void showPopup(yesButton:Boolean, noButton:Boolean, okayButton:Boolean, cancelButton:Boolean) { if(yesButton) { // add YES button } if(noButton) { // add NO Button } // and so on for the rest of the buttons } }

Is this horrible? No. But it is bad, if you're a programmer, to have to look up the order of arguments every time you call the function. It's also annoying - for example, if you only want to show the Cancel button, you have to set all the other to .

Let's use what we learned about & and | to make a better solution:

public class PopupWindow extends Sprite { public static const YES:int = 1; public static const NO:int = 2; public static const OKAY:int = 4; public static const CANCEL:int = 8; public static void showPopup(buttons:int) { if(buttons & YES) { // add YES button } if(buttons & NO) { // add NO button } } }

How would a programmer call the function so the Yes button, No button, and Cancel button are showing? Like this:

PopupWindow.show(PopupWindow.YES | PopupWindow.NO | PopupWindow.CANCEL);

What's going on? It's important to note that our constants in the second example are all powers of two. So, if we look at their binary forms, we will notice they all have one digit equal to 1, and the rest equal to 0. In fact, they each have a different digit equal to 1. This means that no matter how we combine them with |, every combination will give us a unique number. Looking at it in a different way, out result of our | statement will be a binary number with a 1 wherever our options had a 1.

For our current example we have which is equivalent to which rewritten in binary is which gives us a result of .

Now, in our function, we use & to check which options were passed in. For example, when we check , all the bits in YES are equal to 0 except the very rightmost one. So, we are essentially checking if the rightmost bit in buttons is a 1 or not. If it is, will not equal 0 and anything in the if statement will be executed. Conversely, if the rightmost bit in buttons is 0, will equal 0, and the if statement will not be executed.


The ~ Operator

The bitwise NOT operator is slightly different than the two we've looked at so far. Instead of taking an integer on each side of it, it takes an integer only after it. This is just like the ! operator, and, not surprisingly, it does a similar thing. In fact, just as ! flips a boolean from to or vice versa, the ~ operator reverses each binary digit in an integer: from 0 to 1 and 1 to 0:

A quick example. Say we have the integer 37, or 00100101. ~37 is then 11011010. What's the base 10 value of this? Well...


Two's Complement, vs. , and More!

Now the fun begins! We're going to take a closer look at binary numbers on a computer. Let's start with the . As mentioned before, a is typically 4 bytes or 32 bits long, meaning it has 32 binary digits. This is easy to understand: to get the base 10 value we simply convert the number to base 10 regularly. We'll always get a positive number.

But how about the ? It also uses 32 bits, but how does it store negative numbers? If you guessed that the first digit is used to store the sign, you're on the right path. Let's take a look at the two's complement system for storing binary numbers. While we won't go into all the details here, a two's complement system is used because it makes binary arithmetic easy.

To find the two's complement of a binary number, we simply flip all the bits (i.e. do what the ~ operator does) and add one to the result. Let's try this out once:

We then define our result as the value -37. Why do this complicated process and not just flip the very first bit and call that -37?

Well, let's take a simple expression . We all know this should equal 0, and when we add the 37 to its two's complement, that's what we get:

Notice that since our integers only hold eight binary digits, the 1 in our result is dropped, and we end up with 0, as we should.

To recap, to find the negative of a number, we simply take its two's complement. We can do this by inverting all the bits and adding one.

Want to try this yourself? Add to an AS3 file, then compile and run it. You'll see -37 is printed, as it should be.

There is also a little shortcut to do this by hand: starting from the right, work to the left until you reach a 1. Flip all the bits to the left of this first 1.

When we're looking at a signed binary number (in other words, one that can be negative, an not a ), we can look at the leftmost digit to tell whether it's negative or positive. If it's a 0, then the number is positive and we can convert to base 10 simply by calculating its base 10 value. If the leftmost bit is a 1, then the number is negative, so we take the two's complement of the number to get its positive value and then simply add a negative sign.

For example, if we have 11110010, we know it is a negative number. We can find it's two's complement by flipping all the digits to the left of the rightmost 1, giving us 00001110. This equals 13, so we know 11110010 equals -13.


The ^ Operator

We're back to the bitwise operators, and up next is the bitwise XOR operator. There is no equivalent boolean operator to this one.

The ^ operator is similar to the & and | operators in that it takes an or on both sides. When it is calculating the resulting number, it again compares the binary digits of these numbers. If one or the other is a 1, it will insert a 1 in to the result, otherwise it will insert a 0. This is where the name XOR, or "exclusive or" comes from.

Let's take a look at our usual example:

The ^ operator does have uses - it's especially good for toggling binary digits - but we won't cover any practical applications in this article.


The << Operator

We're now on the bitshift operators, specifically the bitwise left shift operator here.

These work a little differently than before. Instead of comparing two integers like &, |, and ^ did, these operators shift an integer. On the left side of the operator is the integer that is being shifted, and on the right is how much to shift by. So, for example, 37 << 3 is shifting the number 37 to the left by 3 places. Of course, we're working with the binary representation of 37.

Let's take a look at this example (remember, we're just going to pretend integers only have 8 bits instead of 32). Here we have the number 37 sitting on its nice block of memory 8 bits wide.

Alright, let's slide all the digits over to the left by 3, as would do:

But now we have a small problem - what do we do with the 3 open bits of memory where we moved the digits from?

Of course! Any empty spots are just replaced with 0s. We end up with 00101000 . And that's all there is to the left bitshift. Keep in mind that Flash always thinks the result of a left bitshift is an , not a . So if you need a for some reason, you'll have to cast it to a like this: . This casting doesn't actually change any of the binary information, just how Flash interprets it (the whole two's complement thing).

An interesting feature of the left bitshift is that it is the same as multiplying a number by two to the shiftAmount-th power. So, . If you can use the left shift instead of , you'll see a huge performance increase.

You may have noticed that the binary number we ended up with did not equal 37 * 8. This is just from our use of only 8 bits of memory for integers; if you try it in ActionScript, you'll get the correct result. Or, try it with the demo at the top of the page!


The >> Operator

Now that we understand the left bitshift, the next one, the right bitshift, will be easy. Everything slides to the right the amount we specify. The only slight difference is what the empty bits get filled with.

If we're starting with a negative number (a binary number where the leftmost bit is a 1), all the empty spaces are filled with a 1. If we're starting with a positive number (where the leftmost bit, or most significant bit, is a 0), then all the empty spaces are filled with a 0. Again, this all goes back to two's complement.

While this sounds complicated, it basically just preserves the sign of the number we start with. So while . I'd recommend trying those out on paper yourself.

Since >> is the opposite of <<, it's not surprising that shifting a number to the right is the same as dividing it by 2 to the power of shiftAmount. You may have noticed this from the example above. Again, if you can use this to avoid calling , you'll get a significant performance boost.


The >>> Operator

Our final bitwise operator is the bitwise unsigned right shift. This is very similar to the regular bitwise right shift, except that all empty bits on the left are filled with 0s. This means the result of this operator is always a positive integer and it always treats the integer being shifted as an unsigned integer. We won't run through an example of this in this section, but we'll see a use for it very shortly.


Using Bitwise Operators to Work With Colors

One of the most practical uses of bitwise operators in Actionscript 3 is working with colors, which are stored typically as .

The standard format for colors is to write them in hexadecimal: 0xAARRGGBB - each letter represents a hexadecimal digit. Here, the first two hexadecimal digits, which are equivalent to the first eight binary digits, represent our alpha, or transparency. The next eight bits represent the amount of red in our color (so an integer from 0 to 255), the next eight the amount of green, and the final eight represent the amount of blue in our color.

Without bitwise operators, it's extremely difficult to work with colors in this format - but with them it's easy!

Challenge 1: Find the amount of blue in a color: Using the & operator, try to find the amount of blue in an arbitrary color.

public function findBlueComponent(color:uint):uint { // Your code here! }

We need a way to 'erase' or mask all the other data in and just have the blue component left. This is easy, actually! If we take - or, written more simply, - we end up with only the blue component.

As you can see from above and you learned in the description of the & operator, any binary digit & 0 will always equal 0, while any binary digit & 1 will keep its value. So if we mask our color by 0xFF which only has 1s where the blue component of our color is located, we end up with just the blue component.

Challenge 2: Find the amount of red in a color: Using two bitwise operators, try to find the amount of red in an arbitrary color.

public function findRedComponent(color:uint):uint { // Your code here! }

We actually have two solutions to this problem. One would be and the other would be

This is very similar to Challenge 1, except that we have to shift our answer at some point.

Challenge 3: Find the transparency of a color: Using only one bitwise operator, try to find the alpha of a color (an integer from 0 to 255).

public function findAlphaComponent(color:uint):uint { // Your code here! }

This one is a touch trickier. We have to be careful with which right shift operater we choose. Because we're working with the leftmost digits of a , we want to use >>> operator. So, our answer simply is .

Final Challenge: Create a color from its components: Using the << and | operators, take the components of a color and merge them in to one .

public function createColor(a:uint, r:uint, g:uint, b:uint):uint { // Your code here! }

Here, we have to shift each component to its correct position, and then merge them. We want Flash to treat it as an unsigned integer, so we cast it to a :


Compound Operators

You may have noticed I have neglected to explain the compound bitwise operators. Imagine we have an integer x. Then, is the same as , is the same as , and so on for the rest of the compound operators.


Conclusion

Thanks for reading this article! I hope you now understand bitwise operators and can utilize them in your AS3 code (or in many other languages!). As always, if you have any questions or comments, please leave them below.

The latest version of this topic can be found at Left Shift and Right Shift Operators (>> and <<).

The bitwise shift operators are the right-shift operator (), which moves the bits of to the right, and the left-shift operator (), which moves the bits of to the left. 1

The following descriptions and examples are valid on Windows for X86 and x64 architectures. The implementation of left-shift and right-shift operators is significantly different on Windows RT for ARM devices. For more information, see the "Shift Operators" section of the Hello ARM blog post.

The left-shift operator causes the bits in to be shifted to the left by the number of positions specified by . The bit positions that have been vacated by the shift operation are zero-filled. A left shift is a logical shift (the bits that are shifted off the end are discarded, including the sign bit). For more information about the kinds of bitwise shifts, see Bitwise shifts.

The following example shows left-shift operations using unsigned numbers. The example shows what is happening to the bits by representing the value as a bitset. For more information, see bitset Class.

If you left-shift a signed number so that the sign bit is affected, the result is undefined. The following example shows what happens in Visual C++ when a 1 bit is left-shifted into the sign bit position.

The right-shift operator causes the bit pattern in to be shifted to the right by the number of positions specified by . For unsigned numbers, the bit positions that have been vacated by the shift operation are zero-filled. For signed numbers, the sign bit is used to fill the vacated bit positions. In other words, if the number is positive, 0 is used, and if the number is negative, 1 is used.

The result of a right-shift of a signed negative number is implementation-dependent. Although Visual C++ uses the sign bit to fill vacated bit positions, there is no guarantee that other implementations also do so.

This example shows right-shift operations using unsigned numbers:

The next example shows right-shift operations with positive signed numbers.

The next example shows right-shift operations with negative signed integers.

The expressions on both sides of a shift operator must be integral types. Integral promotions are performed according to the rules described in Integral Promotions. The type of the result is the same as the type of the promoted .

In the following example, a variable of type is promoted to an .

The result of a shift operation is undefined if is negative or if is greater than or equal to the number of bits in the (promoted) . No shift operation is performed if is 0.

1 The following is the description of the shift operators in the C++ ISO specification (INCITS/ISO/IEC 14882-2011[2012]), sections 5.8.2 and 5.8.3.

The value of is left-shifted bit positions; vacated bits are zero-filled. If has an unsigned type, the value of the result is E2, reduced modulo one more than the maximum value representable in the result type. Otherwise, if has a signed type and non-negative value, and E2 is representable in the corresponding unsigned type of the result type, then that value, converted to the result type, is the resulting value; otherwise, the behavior is undefined.

The value of is right-shifted bit positions. If has an unsigned type or if has a signed type and a non-negative value, the value of the result is the integral part of the quotient of E2. If has a signed type and a negative value, the resulting value is implementation-defined.

Expressions with Binary Operators
C++ Built-in Operators, Precedence and Associativity

shift-expression << additive-expression shift-expression >> additive-expression
#include <iostream> #include <bitset> usingnamespace std; int main() { unsignedshort short1 = 4; bitset<16> bitset1{short1}; // the bitset representation of 4 cout << bitset1 << endl; // 0000000000000100 unsignedshort short2 = short1 << 1; // 4 left-shifted by 1 = 8 bitset<16> bitset2{short2}; cout << bitset2 << endl; // 0000000000001000 unsignedshort short3 = short1 << 2; // 4 left-shifted by 2 = 16 bitset<16> bitset3{short3}; cout << bitset3 << endl; // 0000000000010000 }
#include <iostream> #include <bitset> usingnamespace std; int main() { short short1 = 16384; bitset<16> bitset1{short2}; cout << bitset1 << endl; // 0100000000000000 short short3 = short1 << 1; bitset<16> bitset3{short3}; // 16384 left-shifted by 1 = -32768 cout << bitset3 << endl; // 100000000000000 short short4 = short1 << 14; bitset<16> bitset4{short4}; // 4 left-shifted by 14 = 0 cout << bitset4 << endl; // 000000000000000 }
#include <iostream> #include <bitset> usingnamespace std; int main() { unsignedshort short11 = 1024; bitset<16> bitset11{short11}; cout << bitset11 << endl; // 0000010000000000 unsignedshort short12 = short11 >> 1; // 512 bitset<16> bitset12{short12}; cout << bitset12 << endl; // 0000001000000000 unsignedshort short13 = short11 >> 10; // 1 bitset<16> bitset13{short13}; cout << bitset13 << endl; // 0000000000000001 unsignedshort short14 = short11 >> 11; // 0 bitset<16> bitset14{short14}; cout << bitset14 << endl; // 0000000000000000} }
#include <iostream> #include <bitset> usingnamespace std; int main() { short short1 = 1024; bitset<16> bitset1{short1}; cout << bitset1 << endl; // 0000010000000000 short short2 = short1 >> 1; // 512 bitset<16> bitset2{short2}; cout << bitset2 << endl; // 0000001000000000 short short3 = short1 >> 11; // 0 bitset<16> bitset3{short3}; cout << bitset3 << endl; // 0000000000000000 }
#include <iostream> #include <bitset> usingnamespace std; int main() { short neg1 = -16; bitset<16> bn1{neg1}; cout << bn1 << endl; // 1111111111110000 short neg2 = neg1 >> 1; // -8 bitset<16> bn2{neg2}; cout << bn2 << endl; // 1111111111111000 short neg3 = neg1 >> 2; // -4 bitset<16> bn3{neg3}; cout << bn3 << endl; // 1111111111111100 short neg4 = neg1 >> 4; // -1 bitset<16> bn4{neg4}; cout << bn4 << endl; // 1111111111111111 short neg5 = neg1 >> 5; // -1 bitset<16> bn5{neg5}; cout << bn5 << endl; // 1111111111111111 }
#include <iostream> #include <typeinfo> usingnamespace std; int main() { char char1 = 'a'; auto promoted1 = char1 << 1; // 194 cout << typeid(promoted1).name() << endl; // int auto promoted2 = char1 << 10; // 99328 cout << typeid(promoted2).name() << endl; // int }
#include <iostream> #include <bitset> usingnamespace std; int main() { unsignedint int1 = 4; bitset<32> b1{int1}; cout << b1 << endl; // 00000000000000000000000000000100 unsignedint int2 = int1 << -3; // C4293: '<<' : shift count negative or too big, undefined behavior unsignedint int3 = int1 >> -3; // C4293: '>>' : shift count negative or too big, undefined behavior unsignedint int4 = int1 << 32; // C4293: '<<' : shift count negative or too big, undefined behavior unsignedint int5 = int1 >> 32; // C4293: '>>' : shift count negative or too big, undefined behavior unsignedint int6 = int1 << 0; bitset<32> b6{int6}; cout << b6 << endl; // 00000000000000000000000000000100 (no change)} }
Categories: 1

0 Replies to “Bitwise Shift Left/Right Assignment Definition”

Leave a comment

L'indirizzo email non verrĂ  pubblicato. I campi obbligatori sono contrassegnati *