Bitwise Operators
Bitwise operators perform operations on individual bits of integer values. Unlike Boolean operators (&&, ||, !) which work on logical true/false values, bitwise operators work directly on the binary representation of numbers. They are essential for setting, clearing, and testing individual bits in registers, flags, and hardware control bytes.
& (bitwise AND)
The & operator compares each bit of two values. A result bit is 1 only if both corresponding bits are 1. It is commonly used to mask (clear) specific bits.
void setup() {
Serial.begin(9600);
byte a = 0b11001100;
byte b = 0b11110000;
byte result = a & b;
Serial.println(result, BIN); // prints 11000000
}
void loop() {
// nothing to do here
}
In this example, only bits that are 1 in both a and b remain 1. The lower 4 bits of a are cleared because b has 0s there. Use & with a mask to clear specific bits while preserving others.
| (bitwise OR)
The | operator compares each bit of two values. A result bit is 1 if either corresponding bit is 1. It is commonly used to set specific bits.
void setup() {
Serial.begin(9600);
byte a = 0b00001111;
byte b = 0b11110000;
byte result = a | b;
Serial.println(result, BIN); // prints 11111111
}
void loop() {
// nothing to do here
}
Here, a | b combines the bits โ all 8 bits are set because each position has a 1 in at least one operand. Use | with a mask to set specific bits to 1 without altering other bits.
^ (bitwise XOR)
The ^ operator compares each bit of two values. A result bit is 1 if the corresponding bits are different. XOR is useful for toggling bits โ applying the same mask twice restores the original value.
void setup() {
Serial.begin(9600);
byte a = 0b11110000;
byte toggle = 0b11111111;
byte result = a ^ toggle;
Serial.println(result, BIN); // prints 1111 (00001111)
// XOR twice restores the original
byte restored = result ^ toggle;
Serial.println(restored, BIN); // prints 11110000
}
void loop() {
// nothing to do here
}
In this example, XOR with 0b11111111 flips all bits, inverting the value. XORing again with the same mask restores the original. This makes ^ ideal for toggling LED states or flipping control bits.
~ (bitwise NOT)
The ~ operator inverts all bits of a value โ 1s become 0s and 0s become 1s. It is a unary operator that acts on a single operand.
void setup() {
Serial.begin(9600);
byte a = 0b11110000;
byte result = ~a;
Serial.println(result, BIN); // prints 11111111111111111111111100001111
}
void loop() {
// nothing to do here
}
Note that ~a prints many leading 1s because byte is promoted to int before the NOT operation, and the higher bits of the int are inverted as well. To mask back to a single byte, combine with &: byte result = ~a & 0xFF;. The NOT operator is often used to create masks for clearing bits: flags &= ~(1 << 3); clears bit 3.
<< (left shift)
The << operator shifts all bits of a value to the left by a specified number of positions. Bits shifted off the left end are lost, and zeros fill in on the right. Each left shift multiplies the value by 2.
void setup() {
Serial.begin(9600);
byte x = 1;
for (int i = 0; i < 8; i++) {
Serial.println(x << i, BIN); // prints 1, 10, 100, 1000, ...
}
// Calculate 2^5 using left shift
int power = 1 << 5; // 32
Serial.println(power); // prints 32
}
void loop() {
// nothing to do here
}
This example shows how shifting 1 left by i positions produces powers of two. Left shift is faster than multiplication and is the standard way to create bit masks: 1 << 3 gives 0b00001000, which is useful for targeting bit 3.
>> (right shift)
The >> operator shifts all bits of a value to the right by a specified number of positions. Bits shifted off the right end are lost. For unsigned types, zeros fill in on the left. Each right shift divides the value by 2 (truncating).
void setup() {
Serial.begin(9600);
byte x = 0b11110000;
byte result = x >> 4;
Serial.println(result, BIN); // prints 1111
int value = 256;
for (int i = 0; i < 4; i++) {
value >>= 1; // divide by 2 each time
Serial.println(value); // prints 128, 64, 32, 16
}
}
void loop() {
// nothing to do here
}
This example shifts the upper nibble of x down to the lower nibble, effectively isolating it. Right shift is faster than division and is commonly used to extract specific bit fields from sensor data or register values.