Intermediate Operations

 

Bit Manipulations

 

Introduction

We learned in the previous lesson that, when you declare a variable, the compiler reserves an amount of space in memory for that variable. Indeed, as we learned when studying bytes and words, a declared variable occupies space that resembles a group of small boxes. In our human understanding, it is not always easy to figure out how a letter such as as B is stored in 7 seven small boxes when we know that B is only one letter.

Bit manipulation or a bit related operation allows you to control how values are stored in bits. This is not an operation you will need to perform very often, especially not in the early stages of your C++ journey. Nevertheless, bit operations (and related overloaded operators) are present on all GUI or application programming environments, so much that you should be aware of what they do or what they offer. At this time, you should (must) be aware of what a bit, byte, and a word are, as we saw in the previous lesson.

Bits Operators: The Bitwise NOT Operator ~

One of the operations you can perform on a bit consists of reversing its value. That is, if a bit holds a value of 1, you may want to change it to 0 and vice-versa. This operation can be taken care of by the bitwise NOT operator that is represented with the tilde symbol ~

The bitwise NOT is a unary operator that must be placed on the left side of its operand as in

~Value

To perform this operation, the compiler considers each bit that is part of the operand and inverts the value of each bit from 1 to 0 or from 0 to 1 depending on the value the bit is holding. This operation can be resumed in the following table:

Bit ~Bit
1 0
0 1

Consider a number with a byte value such as 248. In our study of numeric systems, we define how to convert numbers from one system to another (this could be a good time to review or study the numeric systems). Based on this, the binary value of decimal 248 is 1111 1000 (and its hexadecimal value is 0xF8). If you apply the bitwise NOT operator on it to reverse the values of its bits, you would get the following result:

 Value 1 1 1 1 1 0 0 0
~Value 0 0 0 0 0 1 1 1

Comparing Bits: The Bitwise AND Operator &

The bitwise & is a binary operator that uses the following syntax

Operand1 & Operand2

This operator considers two values and compares the bit of each with the corresponding bit of the other value. If both corresponding bits are 1, the comparison produces 1. Otherwise, that is, if either bit is 0, the comparison produces 0. This comparison is resumed as follows:

Bit1 Bit2 Bit1 & Bit2
0 0 0
1 0 0
0 1 0
1 1 1

Imagine you have two byte values represented as 187 and 242. Based on  our study of numeric systems, the binary value of decimal 187 is 1011 1011 (and its hexadecimal value is 0xBB). The binary value of decimal 242 is 1111 0010 (and its hexadecimal value is 0xF2). Let’s compare these two values bit by bit, using the bitwise AND operator:

  Binary Decimal
N1 1 0 1 1 1 0 1 1 187
N2 1 1 1 1 0 0 1 0 242
N1 & N2 1 0 1 1 0 0 1 0 178

Most of the times, you will want the compiler to perform this operation and use the result in your program. This means that you can get the result of this operation and possibly display it on the console. The above operation can be performed by the following program:



    
#include <iostream>
using namespace std;

main()
{
const int N1 = 187;
const int N2 = 242;

cout << N1 << " & " << N2 << " = " << (N1 & N2) << "\n\n";

return 0;
}
This would produce:
187 & 242 = 178
 
 

Comparing Bits: The Bitwise OR Operator |

You can perform another type of comparison on bits using the bitwise OR operator that is represented by |. Its syntax is:

Value1 | Value2

Once again, the compiler compares the corresponding bits of each operand. If at least one of the equivalent bits is 1, the comparison produces 1. The comparison produces 0 only if both bits are 0. This operation is resumed as follows:

Bit1 Bit2 Bit1 | Bit2
0 0 0
1 0 1
0 1 1
1 1 1

Once again, let’s consider decimals 187 and 242. Their bitwise OR comparison would render the following result:

  Binary Decimal
N1 1 0 1 1 1 0 1 1 187
N2 1 1 1 1 0 0 1 0 242
N1 | N2 1 1 1 1 1 0 1 1 251

You can also let the compiler perform the operation and produce a result. Here is an example:



    
#include <iostream>
using namespace std;

main()
{
const int N1 = 187;
const int N2 = 242;

cout << N1 << " | " << N2 << " = " << (N1 | N2) << "\n\n";
return 0;
}

This would produce:

187 | 242 = 251

Comparing Bits: The Bitwise-Exclusive XOR Operator ^

Like the previous two operators, the bitwise-exclusive OR operator performs a bit comparison of two values. It syntax is:

Value1 ^ Value2

The compiler compares the bit of one value to the corresponding bit of the other value. If one of the bits is 0 and the other is 1, the comparison produces 1. In the other two cases, that is, if both bits have the same value, the comparison produces 0. This operation is resumed as follows:

 
Bit1 Bit2 Bit1 ^ Bit2
0 0 0
1 0 1
0 1 1
1 1 0

We will again consider decimals 187 and 242. Their bitwise-exclusive XOR comparison would render the following result:

 
  Binary Decimal
N1 1 0 1 1 1 0 1 1 187
N2 1 1 1 1 0 0 1 0 242
N1 ^ N2 0 1 0 0 1 0 0 1 73

If the compiler performs this operation, it can produce a result as in the following example:

#include <iostream>
using namespace std;

main()
{
	const int N1 = 187;
	const int N2 = 242;

	cout << N1 << " ^ " << N2 << " = " << (N1 ^ N2) << "\n\n";
}

This would produce:

187 ^ 242 = 73

Bit Shift Operators: The Left Shift <<

In the previous lesson, we learned that bits are aligned in some consecutive manner to store data as needed. One operation you can perform on such bits consists of moving bits in a direction of your choice.

C++ provides the left shift operator represented with << and its syntax is:

Value << ConstantInteger

The left shift operator, <<, is a binary operator whose right operand must be a constant integer. When performing the operation, the compiler would “push” Value’s bits to the left by the number of ConstantInteger. The number of ConstantInteger bits on the left side of Value would disappear. The bits on the left side of Value would replace them. After moving to the left, the space left by the most right bits would be filled with 0 bits.

Imagine you have a variable named Value and whose value is 42. The binary value of 42 is 0010 1010 (you are probably asking, "Why the hell do I need to know this?" and my answer is, "I have no idea" but you ain't got no choice; ain't no one else gonna learn this stuff for you). Imagine you want to shift Value to the left by 2 bits. You would proceed as follows:

    0 0 1 0 1 0 1 0
42 Shifted to the left by 2 bits << 2
0 0 1 0 1 0 1 0 0 0

The resulting binary number is 1010 1000 and its decimal value is  1*27 + 0*26 + 1*25 + 0*24 + 1*23 + 0*22 + 0*21 + 0*20 = 1*128 + 0*64 + 1*32 + 0*16 + 1*8 + 0*4 + 0*2 + 0*1 = 128 + 0 + 32 + 0 + 8 + 0 + 0 + 0 = 128 + 32 + 8 = 168

This can also be illustrated in the following program:

#include <iostream>
using namespace std;

main()
{
	const int Value = 42;

	cout << Value << " << 2 = " << (Value << 2) << "\n\n";
}

This would produce:

42 << 2 = 168

Bit Shift Operators: The Right Shift >>

As opposed to the left operator, the right shift moves bits to the right by a natural number. Everything is done as for the left shift except that the concept is applied to the opposed direction.

Therefore, if you shift 42 to the right, the binary result would be 0000 1010, whose decimal value is 10.

 

Inline Assembly

 

Introduction

All the variables that we have used so far were declared in, and passed to, the random memory (RAM). Once a variable is declared and “put” in the memory, whenever it is involved in a calculation or assignment, the microprocessor sends a request to the memory to retrieve the value of the variable.

The Central Processing Unit (CPU), also called the microprocessor, has its own memory. The microprocessor is made of memory cells called registers. Unlike the memory in the RAM, the access of the memory in the microprocessor is more precise; so precise that the registers are referred to by using their names. Some of the most commonly used registers (also called general purpose registers) are called EAX, EBX, ECX, EDX, ESI, etc. These registers are mostly used in the Assembly Language for low-level programming.

Most modern compilers allow you to include Assembly Language code in your program. Using this feature, you can write a section or sections of Assembly language. When Assembly is included in your C++ program, it is referred to as Inline Assembly.

Passing Values to Registers

Using registers allows the programmer to write assignments directly destined for the microprocessor. The assignments and operations in the Assembly language are called instructions. When instructions are used by registers, the processing of the program is fast because the microprocessor does not have to retrieve the values of the variables in the RAM; these values, since existing in the registers, are readily available.

A section that has Assembly code starts with __asm followed by some other techniques. When the compiler encounters this keyword, it knows that the subsequent code would be in Assembly language and it would treat it accordingly. For example, instead of performing a calculation in the RAM, the following program will assign values to two integer variables, namely Number1 and Number2, then it calculate their sum of those two numbers and stores the result in another variable called Result. After the calculation, the Assembly section sends the result back to the C++ compiler to display the variables and their values:

#ifdef __BORLANDC__

  #pragma argsused

#endif



#include <iostream>

using namespace std;



int main( int argc, char * argv[] )

{

    int Number1, Number2, Result;



    __asm

    {

        MOV Number1, 248 // Initialize Number1

        MOV Number2, 405 // Initialize Number2

        MOV EAX, Number1 // Put the value of Number1 in the EAX register

        ADD EAX, Number2 // Add the value of Number2 to the content of EAX

        MOV Result, EAX  // Put the content of EAX into Result

    }                    // That's it



    cout << "Number1 = " << Number1 << endl;

    cout << "Number2 = " << Number2 << endl;

    cout << "\nAfter adding Number1 to Number2," << endl;

    cout << "Result  = " << Result << endl;



    return 0;

}

This would produce:

umber1 = 248

Number2 = 405



After adding Number1 to Number2,

Result  = 653
 

Previous Copyright © 2004-2009 FunctionX, Inc. Next