Have you ever come across some code that uses strange symbols like &
, |
, and ^
? These are actually bitwise operators, and while they may look intimidating, they're much simpler than they seem. In fact, bitwise operations can be a powerful tool for solving some tricky programming problems, and they can be underutilized in some cases. In this article, we'll take a closer look at bitwise operations and explore some practical examples of how they can be used to solve problems with JavaScript.
Bitwise AND is a binary operator that compares the bits of two values and returns a new value where each bit is set if and only if the corresponding bits in both original values are set. In other words, it returns a 1
only for the bit positions where both operands have a 1
.
Let's say we have two numbers: 6
and 5
. In binary, these numbers are represented as 110
and 101
, respectively. Let's apply the bitwise AND operation between them:
The first bit of 110
is 1
and the first bit of 101
is 1
. Since both bits are 1
, the corresponding bit in the result is also 1
.
The second bit of 110
is 1
and the second bit of 101
is 0
. Since one of the bits is 0
, the corresponding bit in the result is also 0
.
The third bit of 110
is 0
and the third bit of 101
is 1
. Since one of the bits is 0
, the corresponding bit in the result is also 0
.
So the result of 110 AND 101
is 100
. Therefore, 6 & 5 = 4
.
To determine whether a number is even or odd we can use bitwise AND. In this case, we check the least significant bit (LSB) of the binary representation of the number. If the LSB is 1, the number is odd. If it is 0, the number is even.
function isEven(number) {
return (number & 1) === 0;
}
In this example, we use the bitwise AND operator (&) to compare the number with the binary value of 1 (00000001
). Since the LSB of an odd number is 1
, the result of the bitwise AND operation will also be 1
. For even numbers, the LSB is 0
, so the result of the bitwise AND operation will also be 0
. We then check whether the result is equal to 0
to determine whether the number is even or odd.
The bitwise OR (|) operator is a binary operator that compares two values bitwise and returns a new value with a 1
in each bit position where at least one of the corresponding bits of the original values is 1
, and a 0
where both are 0
.
Let's say we have two numbers: 6
and 5
. Their binary equivalent are 110
and 101
. Let’s apply the bitwise OR operator to them step by step:
110
is 1
and the first bit of 101
is 1
. Since both bits are 1
, the corresponding bit in the result is also 1
.110
is 1
and the second bit of 101
is 0
. Since one of the bits is 1
, the corresponding bit in the result is 1
.110
is 0
and the third bit of 101
is 1
. Since one of the bits is 1
, the corresponding bit in the result is 1
.
So the result of 110 OR 101
is 111
. In other words, 6 | 5 = 7
.
Suppose we have a set of user permissions encoded as binary values. We can use the bitwise OR operator to combine these permissions together into a single value.
For example, let's say we have three permissions:
0001
0010
0100
If a user has all three permissions, their permission value would be 0111
const READ_PERMISSION = 0b0001; // 1
const WRITE_PERMISSION = 0b0010; // 2
const EXECUTE_PERMISSION = 0b0100; // 4
const superUserPermissions = READ_PERMISSION | WRITE_PERMISSION | EXECUTE_PERMISSION;
XOR (exclusive or) is a bitwise operator that compares two values bitwise and returns a new value with a 1 in each bit position where the corresponding bits of the original values are different, and a 0 where they are the same.
Let's say we have two numbers: 6
and 5
. Their binary equivalent are 110
and 101
. Let’s apply XOR to them step by step:
110
is 1
and the first bit of 101
is 1
. Since both bits are the same, the corresponding bit in the result is 0
.110
is 1
and the second bit of 101
is 0
. Since the bits are different, the corresponding bit in the result is 1
.110
is 0
and the third bit of 101
is 1
. Since the bits are different, the corresponding bit in the result is 1
.So the result of 110 XOR 101
is 011
. In other words, 6 ^ 5 = 3
.
Suppose you have an array of integers, where every integer appears twice except for one. You can use XOR to find the unique integer by XOR-ing each integer in the array with a running XOR sum. Here's an implementation:
function findUniqueNumber(arr) {
let uniqueNumber = 0;
for (let i = 0; i < arr.length; i++) {
uniqueNumber ^= arr[i];
}
return uniqueNumber;
}
In this implementation, we initialize uniqueNumber
to 0
and XOR it with each integer in the array. Since XOR-ing a number with itself results in 0
, the final value of uniqueNumber
will be the unique integer in the array.
NOT (~) is a bitwise operator that takes a single operand and inverts all of its bits. Specifically, it changes each 0 to a 1 and each 1 to a 0. When you use the (~) operator, it first converts its operand to a 32-bit signed integer, and then performs a bitwise negation on that value.
Let's say we have the number 5
, whose binary equivalent is 0000000000000101
. When the NOT operator is applied to this number, it flips all the bits to get the binary number 1111111111111010
, which represents the decimal value -6
in two's complement notation. The leftmost bit in the binary number represents the sign of the number, where 0 represents a positive number and 1 represents a negative number.
Suppose we want to flip the sign of a number
let x = 5;
x = ~x + 1; // -5
In this example, we first flip the bits of x
using the NOT operator, then add 1 to obtain the two's complement representation of -5
.
The left shift (<<) and right shift (>>) operators are used to shift the bits of a number to the left or right by a specified number of positions. When you shift bits to the left, zeros are added to the right side of the binary representation of the number, while when you shift bits to the right, the bits are shifted to the right and the left side is padded with zeros or ones, depending on whether the number is positive or negative.
For example, if we have the number 5
, whose binary representation is 00101
, and we shift it to the left by two positions, we get 10100
, which is the binary representation of the number 20
. If we shift it to the right by one position, we get 00010
, which is the binary representation of the number 2
.
The shift operators (<<) and (>>) can be used to perform fast multiplication and division by powers of 2
, which are commonly used operations in computer programming.
To multiply a number by 2^k
, you can use the left shift operator (<<) to shift the bits of the number k
positions to the left. Each position shift effectively multiplies the number by 2
, so shifting k
positions is equivalent to multiplying by 2^k
const x = 5;
const k = 2;
const result = x << k; // 20
Similarly, to divide a number by 2^k
, you can use the right shift operator (>>) to shift the bits of the number k
positions to the right. Each position shift effectively divides the number by 2
, so shifting k
positions is equivalent to dividing by 2^k
const x = 20;
const k = 2;
const result = x >> k; // 5
The unsigned right shift operator (>>>) is a bitwise operator that shifts the bits of a number to the right by a specified number of positions. Unlike the right shift operator (>>), the unsigned right shift operator always fills the leftmost positions with zeros, regardless of whether the number is positive or negative.
For example, if we have the number -5
, whose binary representation is 11111011
, and we shift it to the right by two positions using the signed right shift operator(>>), we get 11111110
, which is the binary representation of the number -2
. However, if we use the unsigned right shift operator, we get 00111110
, which is the binary representation of the number 62
.
JavaScript does not have a built-in unsigned integer data type, so to convert a signed integer to an unsigned integer, you can use the (>>>) operator. This is because the (>>>) operator always fills the leftmost bit with a zero, effectively converting the number to an unsigned integer.
const x = -5;
const y = x >>> 0; // 4294967291
I hope this article has helped demystify bitwise operations for you and given you a better understanding of their potential power and usefulness in programming. Don't hesitate to experiment with them in your own projects and see how they can help you achieve your programming goals!