Understanding Bitwise Operators

Photo by Andrew Neel on Unsplash

Understanding Bitwise Operators

A Practical Introduction for Beginners

Prerequisite: To understand the bitwise operators and use them like a pro, It is expedient to have prior knowledge of Binary Numbers, decimal-to-binary conversions, comparison, relational and logical and Mathematical operators. Here are notable articles to help you,

What are Binary numbers

Conversion of decimal to binary

In this article, I unveiled the mysteries of bitwise operators and in the end, you will become conversant and you will be able to work with bitwise operators.

Introduction

Bitwise operators allow the manipulation of data at its lowest level. That is, in the binary format.

As humans, we don’t process data in 0’s and 1’s, thus, it isn't very clear for programmers to work with these operators or even visualize their usefulness. This causes one to think,

“Oh, These are not a great deal, I don't have to understand them because I might never have to use them to implement a real-life application!

I equally shared this thought process. But it is quite a misconception. Knowing the applications of bitwise operations will be an essential skill set in the long run and bitwise operations are applied in popular programming languages including C, Java, Python and Pascal.

What is a bitwise operation?

Bitwise operations are computational operations that involve working on individual bits of binary numbers. Bitwise operations permit the comparison of corresponding bits using operators to give out results.

Operators that allow the manipulation of bits of binary numbers are called bitwise operators. Unlike other operators, bitwise operators manipulate the binary representation of numbers. Numbers are implicitly converted into binary form by these operators, worked upon and the result is displayed.

Types of bitwise operators

  1. The AND operator (&)

  2. The XOR operator ( ^ )

  3. The OR operator ( | )

  4. THE NOT operator ( ~ )

  5. The RIGHT SHIFT operator (>>)

  6. The LEFT SHIFT operator (<<)

Bitwise AND operator (&)

The bitwise AND operator is denoted by an ampersand symbol &. It performs a bitwise operation between corresponding bits of digits. The logic behind & operator produces a result:

1: if both bits are 1

0: if either of the corresponding bit is 0

let num = 5; // (4-bits) binary format 0101
let num2 = 4; // binary format 0100
let and = num & num2;
console.log(and) //output 4
console.log(and.toString(2)) //output 0100

Here, & operator implicitly compares the numbers at their bits level, performs the operations and produces the result. The method toString() specifies a radix for converting numeric values to strings. The argument (2) converts the decimal number to its base-2 system, also known as a binary system.

The truth table below illustrates the binary representation of AND Operation.

545 & 4
000
111
000
100

The Bitwise XOR Operator ( ^ )

The XOR is an acronym for Exclusive OR. It is denoted by the caret symbol ^ .The ^ operator performs computation logic on the corresponding bit by producing a result:

1: If corresponding bits are 0 and 1

0: if the corresponding bit consists of both 1 and 1 or if it consists of both 0 and 0

let num  = 5;  //Binary format 101
let num2 = 4;   //Binary format 100
let xor = num ^ num2;

console.log(xor); //output 1
console.log(xor.toString(2));  //output 1

It reviews whether the bits are of the same binary digit when comparing each pair of bits. If they are not the same. It produces a new binary digit 0 . Otherwise, 1 is produced.

The truth table below illustrates the XOR operation.

545 ^ 4
000
110
000
101

Bitwise OR operator ( | )

The bitwise OR operator is denoted by a pipe symbol | . Also called a vertical bar. It performs a bitwise operation on corresponding bits of digits. The logic behind | operator produces a result:

1: if either of the corresponding bit is 1

0: if neither of the bits is 1

let num = 5; // binary format 0101
let num2 = 4; // binary format 0100
let or = num | num2;

console.log(or) //output 5
console.log(or.toString(2)) //output 0101

The | operator compares the binary format of variable num and num2 and subsequently produces a new binary digit. The bitwise AND operator will produce a result 1 if either of the corresponding bit is 1. Otherwise, 0 is produced.

The truth table below illustrates the binary representation of OR Operation.

545 or 4
000
111
000
101

Bitwise NOT Operator ( ~ )

The bitwise NOT operator is denoted by the Tilda symbol ~ . It is also called the complement operator. In Mathematics and Statistics, It is known as an approximation symbol. It is a unary operator. A unary operator works on a single operand. The bitwise NOT operator performs a bitwise negation operation on each bit. It works by flipping individual bits.

let decimal = 5;
let binary = decimal.toString(2);
let flippedBinary = (~decimal).toString(2);

console.log(~decimal); // -6
console.log(binary);  //Binary format 0101
console.log(flippedBinary);  // Binary format -110

The code above initialized a variable decimal with 5 as the value, another variable binary is initialized and its value is a reference to decimal in its binary format. flippedBinary is also initialized as the negation version of the value of binary.

The result of the ~ operator on a number is calculated as follows:

  • Convert the number to a 32-bit signed integer representation (using two's complement representation).

  • Flip each bit of the binary representation.

  • Obtain the result as the new 32-bit signed integer representation.

P.S Signed integers are data types that represent positive and negative whole numbers.

Let's take another example to illustrate this:

let decimal = 10;  //(32-bits) Binary format: 00000000000000000000000000001010
let flipped = ~decimal;

console.log(flipped);  // Output: -11

In this example, the decimal number 10 is represented as 32-bit: 00000000000000000000000000001010 in binary. When the bitwise NOT operator ~ is applied, it flips each bit:

The result is the binary representation 11111111111111111111111111110101, which corresponds to the signed integer -11 in decimal representation.

So, while the ~ operator doesn't flip the binary representation itself, it flips the individual bits, resulting in the bitwise complement of the number.

It is important to note that the leftmost bit of the negation result above, implies that the result is negative. The rest of the bits however are flipped proportionately.

The correct conversion process from binary to decimal, when dealing with signed integers in two's complement representation, is as follows:

  1. If the leftmost (most significant) bit is 0, the number is positive, and you can convert it to decimal typically.

  2. If the leftmost bit is 1, the number is negative in the two's complement representation.

    • Invert all the bits of the number.

    • Add 1 to the resulting value.

    • Apply a negative sign.

    Original: 00000000000000000000000000001010 // 10
    Flipped:  11111111111111111111111111110101 // -11

The truth table below illustrates the bitwise NOT operation.

10NOT ~
01
10
01
10
01

Let's find the ~ operation on -11 to see if it corresponds to 10.

 let decimal = -11;  // Binary representation: 1111111111111111111111111111011
let flipped = ~decimal;

console.log(flipped);  // Output: 10

That's how the negation operator rolls. You might want to observe this table below:

Decimal  Binary (4-bit)  Two's Complement (4-bit)
   0     0000             0000
   1     0001             0001
   2     0010             0010
   3     0011             0011
   4     0100             0100
   5     0101             0101
   6     0110             0110
   7     0111             0111
  -8     -               1000
  -7     -               1001
  -6     -               1010
  -5     -               1011
  -4     -               1100
  -3     -               1101
  -2     -               1110
  -1     -               1111

The Right-Shift Operator ( >> )

A double greater-than symbol denotes the right-shift operator >> . This operator takes two operands:

  1. The number to be shifted.

  2. The number of times it should be shifted to the right.

Here is the syntax.

result = number to be shifted >> number of times to be shifted.

Here is how >> works.

  1. Takes and implicitly converts the numbers to their binary format.

  2. Individual bits of the binary representation is shifted to the right.

  3. The vacant positions left by shifted bits are filled with zero.

  4. The result is equal to the new positions of the shifted bits.

The result is implicitly converted back to decimals and displayed.

let number = 10; // Binary format (32-bits): 00000000000000000000000000001010
let shift = 2;
let result = number >> shift;
console.log(result); // Output: 2

In this example, the number 10 is represented as 00000000000000000000000000001010 in binary. When >> is applied with a shift value of 2, the binary representation is shifted 2 positions to the right:

Original:  00000000000000000000000000001010
Shifted:   00000000000000000000000000000010

The resulting binary representation is 00000000000000000000000000000010, which corresponds to the decimal value 2.

The Left-Shift Operator (<<)

A double less-than symbol denotes the left-shift operator << . This operator takes two operands:

  1. The number to be shifted.

  2. The number of times it should be shifted to the left.

Here is the syntax.

result = number to be shifted << number of times to be shifted.

The logic behind the workings of the left-shift operator is similar to that of the right-shift operator in terms of the shifting of bits. Here is how << works:

  1. Takes and implicitly converts the numbers to their binary format.

  2. Individual bits of the binary representation are shifted to the left.

  3. The vacant positions left by shifted bits are filled with zero.

  4. The result is equal to the new positions of the shifted bits.

The result is implicitly converted back to decimals and displayed.

For example:

let number = 10; // Binary format (32-bits): 00000000000000000000000000001010
let shift = 2;
let result = number << shift;
console.log(result); // Output: 40

In this example, the number 10 is represented as 00000000000000000000000000001010 in binary. When the left-shift operator << is applied with a shift value of 2, the binary representation is shifted two positions to the left:

Original:  00000000000000000000000000001010
Shifted:   00000000000000000000000000101000

The resulting binary representation is 0000000000000000000000000000101000, which corresponds to the decimal value 40.

Left-shift operators are similar to right-shift operators with a subtle difference in the direction in which the second operand is to be shifted.

Left-shift: The second operand shifts to the left.

Right-shift: The second operand shifts to the right.

Use Cases of Bitwise Operations

Bitwise operations are primarily used in low-level programming and embedded systems and situation that involves memory management. Here are some use cases:

  1. Cryptography: Various cryptographic algorithms use bitwise operations to perform operations such as encryption, decryption, and key generation.

  2. Memory Management: By simultaneously processing multiple values or working with large sets of binary data, bitwise operations can be rendered more efficient in terms of memory consumption and execution time.

  3. Manipulation of individual bits in binary data: Bitwise operations allow you to manipulate individual bits of binary data, which is useful for tasks like setting, clearing, toggling, or checking specific bits.

Although Bitwise operations are rarely used in high-level Programming, understanding them gives you more insight into how the computer works. Likewise, the knowledge can be valuable in certain scenarios where you need fine-grained control over binary data or need to optimize performance in specific situations.