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,
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
The AND operator (&)
The XOR operator ( ^ )
The OR operator ( | )
THE NOT operator ( ~ )
The RIGHT SHIFT operator (>>)
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.
5 | 4 | 5 & 4 |
0 | 0 | 0 |
1 | 1 | 1 |
0 | 0 | 0 |
1 | 0 | 0 |
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.
5 | 4 | 5 ^ 4 |
0 | 0 | 0 |
1 | 1 | 0 |
0 | 0 | 0 |
1 | 0 | 1 |
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.
5 | 4 | 5 or 4 |
0 | 0 | 0 |
1 | 1 | 1 |
0 | 0 | 0 |
1 | 0 | 1 |
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:
If the leftmost (most significant) bit is 0, the number is positive, and you can convert it to decimal typically.
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.
10 | NOT ~ |
0 | 1 |
1 | 0 |
0 | 1 |
1 | 0 |
0 | 1 |
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:
The number to be shifted.
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.
Takes and implicitly converts the numbers to their binary format.
Individual bits of the binary representation is shifted to the
right
.The vacant positions left by shifted bits are filled with zero.
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:
The number to be shifted.
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:
Takes and implicitly converts the numbers to their binary format.
Individual bits of the binary representation are shifted to the
left
.The vacant positions left by shifted bits are filled with zero.
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:
Cryptography: Various cryptographic algorithms use bitwise operations to perform operations such as encryption, decryption, and key generation.
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.
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.