You can also find all 40 answers here π Devinterview.io - Bit Manipulation
The term "bit" stems from "binary" and "digit." As the basic unit of information in computing and digital communications, a bit can assume one of two values: 0 or 1.
Computers operate using a binary number system, employing just two numerals: 0 and 1. In contrast, our day-to-day decimal system is base-10, utilizing ten numerals (0-9).
In the binary system:
- Bit: Represents 0 or 1
- Nibble: Comprises 4 bits, representing 16 values (0-15 in decimal)
- Byte: Contains 8 bits, representing 256 values (0-255 in decimal)
For instance, the decimal number 5 is depicted as
Bits are pivotal in bit manipulation, a field encompassing operations like bit shifting, logical operations (AND, OR, XOR), and bit masking. These techniques find applications in data compression, encryption, and device control.
Considering two 8-bit numbers:
An integer's representation typically occupies a fixed number of bits. On many systems, an integer uses 32 bits. Thus, a 32-bit signed integer spans
Although bits underpin computing, hardware designs can constrain their usage. A 32-bit CPU processes 32 bits simultaneously, requiring extra steps for larger numbers. This led to the adoption of "double words" and "quad words" to represent larger integers.
A byte is a foundational data unit in computing and telecommunications, capable of representing 256 unique values, ranging from 0 to 255. It consists of 8 bits, the smallest data storage units, which can be either 0 or 1.
Each bit in a byte has a place value, starting from the least significant bit (LSB) on the right to the most significant bit (MSB) on the left. Their place values are:
Place Value | Bit Position |
---|---|
128 | 7 |
64 | 6 |
32 | 5 |
16 | 4 |
8 | 3 |
4 | 2 |
2 | 1 |
1 | 0 |
Setting all bits to 1 yields the byte's maximum value of 255.
To find the decimal equivalent of a byte, sum the place values of bits set to 1. For a byte with all bits as 1:
Here is the Python code:
def byte_to_decimal(byte_str):
# Reverse the string for right-to-left calculation
byte_str = byte_str[::-1]
# Sum up place values for bits set to 1
return sum(int(byte_str[i]) * 2 ** i for i in range(len(byte_str)))
# Example usage
byte_value = "11111111" # All bits are 1
decimal_value = byte_to_decimal(byte_value)
print(decimal_value) # Output: 255
Bitwise operations are actions applied to individual bits within binary numbers or data units like integers. These operations offer several advantages, including speed and memory efficiency, and are widely used in specific computing scenarios.
-
Speed: Executing bitwise operations is often faster than using standard arithmetic or logical operations.
-
Memory Efficiency: Operating at the bit level allows the storage of multiple flags or small integers within a single data unit, optimizing memory usage.
-
Low-Level Programming: These operations are crucial in embedded systems and microcontroller programming.
-
Data Manipulation: Bitwise operations can selectively alter or extract specific bits from a data unit.
-
AND (
&
): Yields1
if corresponding bits are both1
; otherwise,0
.- Example:
5 & 3 = 1
.
- Example:
-
OR (
|
): Yields1
if one or both corresponding bits are1
; otherwise,0
.- Example:
$5 | 3 = 7$ .
- Example:
-
XOR (
^
): Yields1
when corresponding bits differ; otherwise,0
.- Example:
$5 \oplus 3 = 6$ .
- Example:
-
NOT (
~
): Inverts all bits.- Example:
$~5$ becomes$-6$ in 2's complement.
- Example:
-
Left Shift (
<<
): Moves bits to the left and fills in with0
.- Example:
$5 \text{ << 2 } = 20$ .
- Example:
-
Right Shift (
>>
): Moves bits to the right and fills in based on the sign of the number.- Example:
$5 \text{ >> 2 } = 1$ .
- Example:
-
Zero Fill Right Shift (>>>): Shifts bits right, filling in zeros.
- Example:
$-5 \text{ >>> 2 } = 1073741823$ .
- Example:
-
Ones' Complement: Similar to NOT but restricted to 32 bits.
- Example:
(~5) & 0xFFFFFFFF
.
- Example:
-
Twos' Complement: Used for representing negative numbers in two's complement arithmetic.
- Example:
$~5 + 1 = -6$ .
- Example:
-
Flag Management: Bits can represent on/off flags, and bitwise operators can be used to toggle these.
-
Data Compression: These operations play a role in compression algorithms.
-
Encryption: Bitwise manipulation is used in cryptographic algorithms.
Here is the Python code:
# Define flags with binary representation
FLAG_A, FLAG_B, FLAG_C, FLAG_D = 0b0001, 0b0010, 0b0100, 0b1000
# Set flags B and D
flags = FLAG_B | FLAG_D
# Check if Flag C is set
print("Flag C is set" if flags & FLAG_C else "Flag C is not set")
Bitwise operators provide efficient means of manipulating variables at the bit level. This feature is integral to various applications like data compression, cryptography, and embedded systems.
- Run-Length Encoding: Identical consecutive characters are stored once followed by a count. This requires bitwise operators to efficiently manage the bit stream.
- Huffman Coding: For implementing lossless data compression, this technique assigns shorter codes to frequently occurring characters, which is made possible through bitwise operations.
- Bit Level Encryption: Techniques like XORing bits are used in various encryption algorithms.
- Hardware Security: In integrated chips, bitwise operations play a crucial role in providing secure key management systems.
- Packet Inspection: Applications, especially in firewalls and routers, might necessitate bitwise operations for quick and low-level packet analysis.
- Peripheral Configuration: The individual bits in control registers, often set using bitwise operations, help configure peripherals in microcontrollers and other embedded systems.
- Memory Mapped I/O: Bitwise operations are instrumental in interfacing with embedded hardware through memory-mapped I/O.
- Bit Manipulation for Speed: In specific situations, using bit-level operations can significantly enhance the efficiency of algorithms. This is especially true for resource-constrained devices.
- Integer Multiplication and Division in Limited-Bit Environments: On systems with limitations on the size of numbers that can be represented, bit manipulation can be used to carry out basic arithmetic operations more efficiently.
- Pixel Manipulation: Adjusting color information or applying specific transformations may involve bitwise operations in image processing.
- Optimized Blending: Quick and optimized alpha blending, common in graphic rendering, can be achieved using bitwise operations without the need for costly division and multiplication.
- Flags in Data Structures: Bitwise operations enable data integrity checks and the management of multiple flags within data structures while using limited memory.
- Parity Checks: Detection of odd/even parity in small data segments, commonly in error-checking algorithms, employs bitwise methods.
- Memory Allocation: In scenarios where individual bits are used for encoding specific information within memory allocation strategies, bitwise operations are fundamental.
- Cache Optimization: Techniques like bit masking can be used to optimize cache performance by ensuring data alignment with cache lines.
- Keyboard Input Handling: In certain contexts, handling multiple keyboard inputs or mapping specific keys can be simplified using bit manipulation.
- Graphics Display: To save resources while still effectively managing color palettes in limited environments, bit manipulation is employed.
- Memory and Resource Allocation: In operating systems and embedded systems, bitwise operations provide a means of managing the allocation and deallocation of resources with precision and efficiency.
- Memory Efficiency: Bit fields in languages like C and C++ make efficient use of memory by grouping variables into compact memory units.
- Performance Enhancement in Math Operations: Bit manipulation can be used for efficient multiplication, division, and modulo operations on binary integers.
- Finding Mismatches and Duplicates: Bitwise Exclusive OR (XOR) operations ascertain duplicates or mismatches in data sets.
Here is the Python code:
def run_length_encode(data):
encoded = []
count = 1
for i in range(1, len(data)):
if data[i] == data[i - 1]:
count += 1
else:
encoded.append((data[i - 1], count))
count = 1
encoded.append((data[-1], count))
return encoded
def run_length_decode(encoded):
decoded = ""
for char, count in encoded:
decoded += char * count
return decoded
# Test the Run-Length Encoding and Decoding
input_data = "AAABBCCCCDDEEEE"
encoded_data = run_length_encode(input_data)
decoded_data = run_length_decode(encoded_data)
print("Original Data:", input_data)
print("Encoded Data:", encoded_data)
print("Decoded Data:", decoded_data)
In this example, the input string "AAABBCCCCDDEEEE" is run-length encoded and then decoded back to its original form using bit manipulation techniques.
The bitwise AND operation is a fundamental concept in computer science and cryptography. When you apply this operation to two bits, the result is 1 if both bits are 1. At least one bit being 0 results in a zero output. This operation is often used in hashing and encryption algorithms.
In the context of determining whether a number is odd or even, the bitwise AND operation becomes useful.
The basic way to figure out if a decimal number is even or odd, based on its binary representation, is to look at the least significant bit (the rightmost bit):
- If that bit is 1, the number is odd.
- If it's 0, the number is even.
The rule behind this method is that all even numbers end in 0
in binary, and odd numbers end in 1
.
- bitwise AND with 1: Returns 1 if rightmost bit is 1 (indicating odd number), and 0 if it's 0 (indicating even number).
When you do a bitwise AND with a number and 1, you get:
- 1 if both the numbers are 1.
- 0 if the other number is 0.
For an even number 0
. When you take the logical AND with 1
, you actually perform a logical AND with 0
, which results in 0
.
For an odd number 1
. When you take the logical AND with 1
, the operation returns 1
.
-
For n = 5 (binary: 101):
5 & 1
gives 1, indicatingn
is odd. -
For n = 10 (binary: 1010):
10 & 1
gives 0, indicatingn
is even.
Here is the Python code for the operation:
def is_odd(n):
return n & 1 == 1
The function is_odd
checks whether n
is an odd number by using bitwise AND with 1. If the result is 1, the number is odd.
The Bitwise OR operator works at the binary level. It combines two bit sequences. For each position, if either bit is 1, the result has a 1 in that position.
- Input: Two binary numbers, say
a = 1010
andb = 1100
. - Output: Their bitwise OR, denoted by
a | b
, gives1110
.
Here is the Python code:
a = 10 # Binary: 1010
b = 12 # Binary: 1100
result = a | b
print(bin(result)) # Output: 0b1110 (14 in decimal)
The bitwise XOR operator (^) compares each bit of two integers, setting the resulting bit to 1 only when the two input bits are different.
-
Example: 5 (
$101$ )$\text{XOR}$ 3 ($011$ ) = 6 ($110$ ) -
Properties: Commutative:
$A \text{ XOR } B = B \text{ XOR } A$
-
Invert Elements and Undo Pairing: Useful for error checking and data identification.
-
Text/Data Encryption: Employed in ciphers that use bit manipulation for security.
-
Efficiently Swapping Values: Useful for in-place memory operations without using temporary storage.
-
Color Calculations and Image Processing: Commonplace in graphics processing for tasks like filtering.
-
Error Correction in Data Transmission: Ensures data integrity during communication.
-
Modifying Individual Bits in a Register: Efficiently flip specific bits without affecting others.
Here is the Python code:
a, b = 5, 7
a = a ^ b
b = a ^ b
a = a ^ b
print(a, b) # Output: 7, 5
Let's first discuss the different bitwise operations:
-
Setting a Bit involves turning the bit on (to 1), if it's not already.
-
Toggling a Bit changes the bit's state: 1 becomes 0, and vice versa.
-
Clearing a Bit, on the other hand, turns the bit off (to 0).
And here is the table of operations:
Bit State | Operations | Result |
---|---|---|
0 | Set | 1 |
0 | Toggle | 1 |
0 | Clear | 0 |
1 | Set | 1 |
1 | Toggle | 0 |
1 | Clear | 0 |
Here is the code:
In C++:
#include <iostream>
// Set the I-th bit of N
int setBit(int N, int I) {
return N | (1 << I);
}
// Toggle the I-th bit of N
int toggleBit(int N, int I) {
return N ^ (1 << I);
}
// Clear the I-th bit of N
int clearBit(int N, int I) {
return N & ~(1 << I);
}
int main() {
int number = 10; // 0b1010 in binary
int bitPosition = 1;
// Set bit in position bitPosition
int newNumber = setBit(number, bitPosition); // Result: 14 (0b1110)
std::cout << "Number after setting bit: " << newNumber << std::endl;
// Toggle bit in position bitPosition
newNumber = toggleBit(newNumber, bitPosition); // Result: 10 (0b1010)
std::cout << "Number after toggling bit: " << newNumber << std::endl;
// Clear bit in position bitPosition
newNumber = clearBit(newNumber, bitPosition); // Result: 8 (0b1000)
std::cout << "Number after clearing bit: " << newNumber << std::endl;
return 0;
}
Here are the steps visually:
Initial Number: 1010 (10 in decimal)
Bit Position: 1
Shifted 1 to: 0010
Result (OR): 1110 (14 in decimal)
Previous Result: 1110 (14 in decimal)
Bit Position: 1
Shifted 1 to: 0010
Result (XOR): 1010 (10 in decimal)
Previous Result: 1010 (10 in decimal)
Bit Position: 1
Shifted Negation: 1101
Logical AND: 1000 (8 in decimal)
Bit masking involves using bitwise operations to either clear or set specific bits in a binary number.
For instance, if you want to extract the 3rd bit of a number, you would use the bit mask 00001000
(in binary), which is decimal 8.
To extract the num
, you can use a bit mask that has all 0s except for a 1 at the
You can generate this bit mask mask
by left-shifting a 1 by mask
would be 4 in decimal or 00000100
in binary.
Here's an example, using
def extract_nth_bit(num, n):
mask = 1 << (n-1)
return (num & mask) >> (n-1)
To extract the bit, you perform a logical AND of the number with the mask. All bits in mask
are zero, except for the
After using the logical AND, the extracted bit is still in position 1 (
def extract_nth_bit(num, n):
mask = 1 << (n-1)
return (num & mask) >> (n-1)
# Using a number where the 3rd bit is 1
num = 13 # 1101 in binary
print(extract_nth_bit(num, 3)) # Output: 1
Bit shifts, controlled by the <<
(left) and >>
(right) operators, move bits in a binary number. Each shift direction and position has unique behavior.
- Direction: Determines whether bits shift to the left or to the right.
- Operator: Symbolizes the actual shift in the code.
-
Left Shift (<<): Moves bits to the right, effectively multiplying the number by
$2^n$ . -
Right Shift (>>): Moves bits to the left and truncates the remainder, akin to integer division by
$2^n$ in most programming languages.
Let's understand shift operations through examples:
Original Number (in binary): 11001010
-
1-bit Right Shift
1100101 Binary: 1100101 Decimal: 105
-
2-bit Right Shift
110010 Binary: 110010 Decimal: 50
-
3-bit Right Shift
11001 Binary: 11001 Decimal: 25
-
4-bit, full 8-bit, and 10-bit Right Shift All shift operations are readily achieved by further bit truncation.
Note: As your task requires involving multiplication and division, such shift operations lead to an understanding of these mathematical operations in binary number representation.
-
Multiplication By performing a left shift, you are essentially multiplying the number by 2.
110010 Binary: 1100100 Decimal: 100
-
Division Right shifts are akin to dividing a number by powers of 2. A 3-bit right shift divides the given number by
$2^3 = 8$ .13/2^3 = 13/8 = 1, remainder = 5 Based on this example: 11010 Binary: 11010 Decimal: 26
Here is the Python code:
number = 8
right_shifted = number >> 1 # This effectively divides by 2
print(right_shifted) # Output will be 4
Let's look at two shift operators defined in Java: the right shift (>>) operator and the unsigned right shift (>>>) operator, and compare their functionality.
-
Right Shift (
>>
): Moves all bits of the specified numeric value to the right. It fills the leftmost bits with the sign bit (0 for positive, 1 for negative). -
Unsigned Right Shift (
>>>
): Similar to the>>
operator, but it always fills the left-moved positions with zeros, ignoring the sign bit.
Decimal Binary Bits
10 00001010
10 >> 1 00000101 - 5
10 >>> 1 00000101 - 5
--------------------------
-10 11110110
-10 >> 1 11111011 - (-5)
-10 >>> 1 01111011 - 251
Bit rotation refers to shifting the bits of a binary number to the left or right, and wrapping the bits around so that any that "fall off" one end are reintroduced at the other.
In many programming languages, bit shifts are either arithmetic or logical.
- Arithmetic shifts are typically used for signed integers and preserve the sign bit, which means the bit shifted in from the most significant bit becomes the new least significant bit, and bits "shifted off" on the other end are discarded.
- Logical shifts shift all bits, including the sign bit, and always fill in the vacated bit positions with zeros.
Here is C++ code:
#include <iostream>
int main() {
// Explain logical shift
int logicalShiftResult = -16 >> 3;
// Explain arithmetic shift
int arithmeticShiftResult = -16 << 3;
return 0;
}
The task is to count the number of set bits in an integer - the 1s in its binary representation.
One approach is to check each bit and sum them. An optimized solution uses a technique called Brian Kernighan's Algorithm.
It is based on the observation that for any number x
, the value of x & (x-1)
has the bits of the rightmost set 1
unset. Hence, repeating this operation until the number becomes 0
yields the set bit count.
- Initialize a count variable to
0
. - Iterate using a while loop until the number is
0
. - Within each iteration, decrement the number
n
byn & (n-1)
and increment thecount
variable by1
.
-
Time Complexity:
$O(\text{{set bits count}})$ , as it depends on the number of set bits. -
Space Complexity:
$O(1)$
Here is the Python code:
def count_set_bits(n):
count = 0
while n:
n &= (n-1)
count += 1
return count
Here is the C++ code:
int countSetBits(int n) {
int count = 0;
while (n) {
n &= (n - 1);
count++;
}
return count;
}
The task is to design an algorithm that determines whether a given number is a power of two.
Using bit manipulation, we can apply the logical AND operator to swiftly identify powers of two.
- If a number is a power of two, it has exactly one bit set in its binary representation.
- Subtracting 1 from a power of two yields a binary number with all lower bits set.
Combining these properties, we obtain an expression that performs the essential check.
- Check if the number is non-negative.
- Apply the bitwise AND operation between the number and its one's complement (the bitwise negation of the number).
- Determine if the result is zero.
If the result is zero, we confirm the number is a power of two.
-
Time Complexity:
$O(1)$ -
Space Complexity:
$O(1)$
Here is the Python code:
def is_power_of_two(x: int) -> bool:
return x > 0 and (x & (x-1)) == 0
The task is to create a function that would add two numbers without using the +
operator.
There are several methods to add two numbers without using the +
operator, each with its own trade-offs. One common approach is to use bit manipulation.
-
Perform XOR Operation:
- Calculate the bitwise XOR of the two numbers. This produces a number where the set bits represent the positions at which the two numbers have different bits.
0010 (2) XOR 0100 (4) ------------ 0110 (6)
-
Perform AND Operation, then Left Shift:
- Perform bitwise AND of the two numbers and then left shift the result by
1
. This brings forward any 'carry' bits to the appropriate position.
0010 (2) AND 0100 (4) ------------ 0000 (0)
After left shifting by 1:
0000 (0) << 1 ------------ 0000 (0)
- Perform bitwise AND of the two numbers and then left shift the result by
-
Recursion: Apply the addition method to the new XOR result and the AND-left-shifted result. The recursion continues until there are no carry bits left.
0110 (XOR output) 0000 (Carry bits from AND-left-shifted operation)
Next, we perform the addition method to
0110
and0000
, which returns0110
.The final result will be
0110
which equals 6, the sum of 2 and 4.
-
Time Complexity:
$O(\log n)$ where$n$ is the larger of the two numbers. -
Space Complexity:
$O(1)$
Here is the Python code:
def add_without_plus_operator(a, b):
while b:
# Calculate the carry bits
carry = a & b
# Use XOR to get the sum without carry
a = a ^ b
# Left shift the carry to add in the next iteration
b = carry << 1
return a
# Test the function
print(add_without_plus_operator(2, 4)) # Output: 6