Bitwise operators are so essential in low-level programming, they enable developers to manipulate individual bits within binary representations of data. Dart, a versatile and modern programming language, provides several bitwise operators that empower developers to perform bitwise operations efficiently. In this article, we will explore Dart bitwise operators, their functionality, and provide code examples to solidify your understanding.
Understanding Binary Numbers
Before we explore Dart’s bitwise operators, it’s essential to understand binary numbers. In the binary system, numbers are represented using only two digits: 0 and 1. Each digit is a binary digit, or bit. For example, the decimal number 5 is represented as 101 in binary, indicating 2^2 + 2^0.
Dart Bitwise Operators
Bitwise operators operate on individual bits of binary numbers, making them fundamental tools in tasks such as bitmasking, cryptography, and data compression. Dart supports several bitwise operators, each serving a unique purpose.
AND Operator (&)
The AND operator performs a bitwise AND operation on each pair of corresponding bits. The result is 1 only if both bits are 1; otherwise, the result is 0.
void main() {
int a = 5; // Binary: 0101
int b = 3; // Binary: 0011
int result = a & b; // Binary: 0001 (1 in decimal)
print(result);
}
In this example, the AND operator compares each pair of bits in the binary representation of a and b. Only the bits that are set in both a and b contribute to the result.
OR Operator (|)
The OR operator performs a bitwise OR operation on each pair of corresponding bits. The result is 1 if at least one of the bits is 1.
void main() {
int a = 5; // Binary: 0101
int b = 3; // Binary: 0011
int result = a | b; // Binary: 0111 (7 in decimal)
print(result);
}
Here, the OR operator combines the set bits from both a and b, resulting in a new number with all the significant bits preserved.
XOR Operator (^)
The XOR operator performs a bitwise XOR (exclusive OR) operation on each pair of corresponding bits. The result is 1 if the bits are different; otherwise, the result is 0.
void main() {
int a = 5; // Binary: 0101
int b = 3; // Binary: 0011
int result = a ^ b; // Binary: 0110 (6 in decimal)
print(result);
}
In this case, the XOR operator produces a result with bits set only where the corresponding bits in a and b differ.
Swapping Values Without a Temporary Variable
Bitwise XOR (^) can be used to swap the values of two variables without the need for a temporary variable.
void main() {
int a = 10;
int b = 20;
// Before swap
print('a: $a, b: $b'); // Output: a: 10, b: 20
// Swap values using XOR
a = a ^ b;
b = a ^ b;
a = a ^ b;
// After swap
print('a: $a, b: $b'); // Output: a: 20, b: 10
}
This technique capitalizes on the fact that XORing a value with itself results in 0, making it possible to perform a swap without additional storage.
NOT Operator (~)
The NOT operator performs a bitwise NOT operation, flipping each bit. It changes 1s to 0s and 0s to 1s.
void main() {
int a = 5; // Binary: 0101
int result = ~a; // Binary: 1010 (-6 in decimal due to two's complement representation)
print(result);
}
The NOT operator inverts all the bits in the binary representation of a, including the sign bit, resulting in a negative number.
Left Shift (<<)
The left shift operator shifts the bits of a number to the left by a specified number of positions, effectively multiplying the number by 2 raised to the power of the shift count.
void main() {
int a = 5; // binary: 0000 0101
int result = a << 2; // binary: 0001 0100 (20 in decimal)
print(result); // Output: 20
}
In this case, the bits of a are shifted two positions to the left, filling the vacant positions with zeros.
Right Shift (>>)
The right shift operator shifts the bits of a number to the right by a specified number of positions, effectively dividing the number by 2 raised to the power of the shift count.
void main() {
int a = 16; // binary: 10000
int result = a >> 2; // binary: 00100
print(result); // Output: 4
}
In this example, the bits of a are shifted two positions to the right, and the vacant positions on the left are filled with the sign bit.
Unsigned Shift Right Operator (>>>)
While Dart provides the usual signed right shift operator (>>), it also includes an unsigned right shift operator (>>>), a feature not present in some other languages. The unsigned shift right operator fills the vacant bit positions with zeros, regardless of the sign bit. This functionality is available in Dart with a language version of at least 2.14.
void main() {
int a = -10; // Binary: 1111111111111111111111111111111111111111111111111111111111110110
int result = a >>> 2;
// Result: 4611686018427387901 (Binary: 0011111111111111111111111111111111111111111111111111111111111101)
print(result);
}
In this example, the >>> operator shifts the bits of -10 two positions to the right, filling the vacant positions with zeros. This is particularly useful when working with large unsigned integers.
Bit Manipulation for Flags
Bitwise operators are commonly used for setting and checking flags within a single integer variable. Each bit in the variable represents a specific flag, allowing developers to efficiently manage multiple boolean values in a compact form.
void main() {
// Define flags
final int FLAG_A = 1 << 0; // 0001
final int FLAG_B = 1 << 1; // 0010
final int FLAG_C = 1 << 2; // 0100
// Set flags
int flags = 0;
flags |= FLAG_A; // Set FLAG_A
flags |= FLAG_C; // Set FLAG_C
// Check flags
print((flags & FLAG_A) != 0); // Output: true (FLAG_A is set)
print((flags & FLAG_B) != 0); // Output: false (FLAG_B not set)
print((flags & FLAG_C) != 0); // Output: true (FLAG_C is set)
}
In this example, bitwise OR (|) is used to set flags, and bitwise AND (&) is used to check whether a specific flag is set.
Clearing Bits: Resetting Flags
Clearing bits involves resetting specific bits to 0 within an integer variable, typically to deactivate or clear specific flags. Dart provides a straightforward mechanism for achieving this using bitwise AND and NOT operators.
Let’s extend the previous example to demonstrate the process of clearing bits:
void main() {
// Define flags
final int FLAG_A = 1 << 0; // 0001
final int FLAG_B = 1 << 1; // 0010
final int FLAG_C = 1 << 2; // 0100
// Set flags
int flags = 0;
flags |= FLAG_A; // Set FLAG_A
flags |= FLAG_C; // Set FLAG_C
// Clear FLAG_A (reset to 0)
flags &= ~FLAG_A;
// Check flags
print((flags & FLAG_A) != 0); // Output: false (FLAG_A is cleared)
print((flags & FLAG_B) != 0); // Output: false (FLAG_B not set)
print((flags & FLAG_C) != 0); // Output: true (FLAG_C is set)
}
In this extended example, after setting flags initially, the bitwise AND (&) operation is combined with the bitwise NOT (~) operation to clear a specific flag (in this case, FLAG_A). The result is that FLAG_A is reset to 0, while the other flags remain unaffected.
Understanding the clear bits operation is essential when working with flag-based configurations, providing developers with a powerful tool for dynamic and efficient flag management in their Dart applications.
Conclusion
Understanding and effectively utilizing Dart bitwise operators can significantly enhance your programming capabilities, especially in scenarios requiring low-level bit manipulation. Whether you’re working on bitmasking, performance optimization, or cryptographic algorithms, the versatility of bitwise operators in Dart opens up a world of possibilities.
References: