Bit Math with Arduino
Learn about bit math and how to manipulate individual bits in your Arduino sketches.
This article was revised on 2022/09/28 by Hannes Siebeneicher.
Often when programming in the Arduino environment (or on any computer, for that matter), the ability to manipulate individual bits will become useful or even necessary. Here are some situations where bit math can be helpful:
- Saving memory by packing up to 8 true/false data values in a single byte.
- Turning on/off individual bits in a control register or hardware port register.
- Performing certain arithmetic operations involving multiplying or dividing by powers of 2.
In this article, we first explore the basic bitwise operators available in the C++ language. Then we learn how to combine them to perform certain common useful operations. This article is based on the bit math tutorial by CosineKitty.
The Binary System
To better explain the bitwise operators, this tutorial will express most integer values using binary notation, also known as base two. In this system, all integer values use only the values 0 and 1 for each digit. This is how virtually all modern computers store data internally. Each 0 or 1 digit is called a bit, short for binary digit.
In the familiar decimal system (base ten), a number like 572 means 5*10^2 + 7*10^1 + 2*10^0. Likewise, in binary a number like 11010 means 1*2^4 + 1*2^3 + 0*2^2 + 1*2^1 + 0*2^0 \= 16 + 8 + 2 = 26.
It is crucial that you understand how the binary system works in order to follow the remainder of this tutorial. If you need help in this area, one good place to start is the Wikipedia article on the binary system.
Arduino allows you to specify binary numbers by prefixing them with
0b0b11 == 3B0B11111111Bitwise AND
The bitwise AND operator in C++ is a single ampersand,
&10 & 0 == 02    0 & 1 == 03    1 & 0 == 04    1 & 1 == 1In Arduino, the type
int&int1int a =  92;    // in binary: 00000000010111002    int b = 101;    // in binary: 00000000011001013    int c = a & b;  // result:    0000000001000100, or 68 in decimal.Each of the 16 bits in
abc01000100One of the most common uses of bitwise AND is to select a particular bit (or bits) from an integer value, often called masking. For example, if you wanted to access the least significant bit in a variable
xy1int x = 5;       // binary: 1012    int y = x & 1;   // now y == 13    x = 4;           // binary: 1004    y = x & 1;       // now y == 0Bitwise OR
The bitwise OR operator in C++ is the vertical bar symbol,
|&|10 | 0 == 02    0 | 1 == 13    1 | 0 == 14    1 | 1 == 1Here is an example of the bitwise OR used in a snippet of C++ code:
1int a =  92;    // in binary: 00000000010111002    int b = 101;    // in binary: 00000000011001013    int c = a | b;  // result:    0000000001111101, or 125 in decimal.Bitwise OR is often used to make sure that a given bit is turned on (set to 1) in a given expression. For example, to copy the bits from
ab1b = a | 1;Bitwise XOR
There is a somewhat unusual operator in C++ called bitwise exclusive OR, also known as bitwise XOR. (In English this is usually pronounced "eks-or".) The bitwise XOR operator is written using the caret symbol
^|10 ^ 0 == 02    0 ^ 1 == 13    1 ^ 0 == 14    1 ^ 1 == 0Another way to look at bitwise XOR is that each bit in the result is a 1 if the input bits are different, or 0 if they are the same.
Here is a simple code example:
1int x = 12;     // binary: 11002    int y = 10;     // binary: 10103    int z = x ^ y;  // binary: 0110, or decimal 6The
^1y = x ^ 1;   // toggle the lowest bit in x, and store the result in y.Bitwise NOT
The bitwise NOT operator in C++ is the tilde character
~&|1int a = 103;    // binary:  00000000011001112    int b = ~a;     // binary:  1111111110011000 = -104You might be surprised to see a negative number like -104 as the result of this operation. This is because the highest bit in an
intAs an aside, it is interesting to note that for any integer
x~x-x-1At times, the sign bit in a signed integer expression can cause some unwanted surprises, as we shall see later.
Bit Shift Operators
There are two bit shift operators in C++: the left shift operator
<<>>1int a = 5;        // binary: 00000000000001012    int b = a << 3;   // binary: 0000000000101000, or 40 in decimal3    int c = b >> 3;   // binary: 0000000000000101, or back to 5 like we started withWhen you shift a value
xyx << yyx1int a = 5;        // binary: 00000000000001012    int b = a << 14;  // binary: 0100000000000000 - the first 1 in 101 was discardedIf you are certain that none of the ones in a value are being shifted into oblivion, a simple way to think of the left-shift operator is that it multiplies the left operand by 2 raised to the right operand power. For example, to generate powers of 2, the following expressions can be employed:
11 <<  0  ==    12    1 <<  1  ==    23    1 <<  2  ==    44    1 <<  3  ==    85    ...6    1 <<  8  ==  2567    1 <<  9  ==  5128    1 << 10  == 10249    ...When you shift
xyx >> yxxxintx1int x = -16;     // binary: 11111111111100002    int y = x >> 3;  // binary: 1111111111111110This behavior, called sign extension, is often not the behavior you want. Instead, you may wish zeros to be shifted in from the left. It turns out that the right shift rules are different for
unsigned int1int x = -16;               // binary: 11111111111100002    int y = unsigned(x) >> 3;  // binary: 0001111111111110If you are careful to avoid sign extension, you can use the right-shift operator
>>1int x = 1000;2    int y = x >> 3;   // integer division of 1000 by 8, causing y = 125.Assignment Operators
Often in programming, you want to operate on the value of a variable
xxx1x = x + 7;    // increase x by 7Because this kind of thing occurs so frequently in programming, C++ provides a shorthand notation in the form of specialized assignment operators. The above code fragment can be written more concisely as:
1x += 7;    // increase x by 7It turns out that bitwise AND, bitwise OR, left shift, and right shift, all have shorthand assignment operators. Here is an example:
1int x = 1;  // binary: 00000000000000012    x <<= 3;    // binary: 00000000000010003    x |= 3;     // binary: 0000000000001011 - because 3 is 11 in binary4    x &= 1;     // binary: 00000000000000015    x ^= 4;     // binary: 0000000000000101 - toggle using binary mask 1006    x ^= 4;     // binary: 0000000000000001 - toggle with mask 100 againThere is no shorthand assignment operator for the bitwise NOT operator
~x1x = ~x;    // toggle all bits in x and store back in xA word of caution: bitwise operators vs. boolean operators
It is very easy to confuse the bitwise operators in C++ with the boolean operators. For instance, the bitwise AND operator
&&&- They don't calculate numbers the same way. Bitwise 
 operates independently on each bit in its operands, whereas&
 converts both of its operands to a boolean value (&&
 \==1 ortrue
 \==0), then returns either a singlefalse
 ortrue
 value. For example,false
 , because 4 is 100 in binary and 2 is 010 in binary, and none of the bits are 1 in both integers. However,4 & 2 == 0
 , and4 && 2 == true
 numerically is equal totrue
 . This is because 4 is not 0, and 2 is not 0, so both are considered as boolean1
 values.true
- Bitwise operators always evaluate both of their operands, whereas boolean operators use so-called short-cut evaluation. This matters only if the operands have side-effects, such as causing output to occur or modifying the value of something else in memory. Here is an example of how two similar looking lines of code can have very different behavior:
1int fred (int x)2    {3        Serial.print ("fred ");4        Serial.println (x, DEC);5        return x;6    }1void setup()2    {3        Serial.begin (9600);4    }1void loop()2    {3        delay(1000);    // wait 1 second, so output is not flooded with serial data!4        int x = fred(0) & fred(1);5    }If you compile and upload this program, and then monitor the serial output from the Arduino GUI, you will see the following lines of text repeated every second:
1fred 02    fred 1This is because both
fred(0)fred(1)x1int x = fred(0) & fred(1);and replace the bitwise
&&&1int x = fred(0) && fred(1);and compile, upload, and run the program again, you may be surprised to see only a single line of text repeated every second in the serial monitor window:
1fred 0Why does this happen? This is because boolean
&&falsefalseint x = fred(0) && fred(1);1int x;2    if (fred(0) == 0) {3        x = false;  // stores 0 in x4    } else {5        if (fred(1) == 0) {6            x = false;  // stores 0 in x7        } else {8            x = true;   // stores 1 in x9        }10    }Clearly, the boolean
&&As with bitwise AND and boolean AND, there are differences between bitwise OR and boolean OR. The bitwise OR operator
|||false|||Putting it all together: common problems solved
Now we start exploring how we can combine the various bitwise operators to perform useful tasks using C++ syntax in the Arduino environment.
A word about port registers in the Atmega8 microcontroller
Usually when you want to read or write to digital pins in the Atmega8, you use the built-in functions digitalRead() or digitalWrite() supplied by the Arduino environment. Suppose that in your
setup()1void setup()2    {3        int pin;4        for (pin=2; pin <= 13; ++pin) {5            pinMode (pin, OUTPUT);6        }7        for (pin=2; pin <= 10; ++pin) {8            digitalWrite (pin, LOW);9        }10        for (pin=11; pin <= 13; ++pin) {11            digitalWrite (pin, HIGH);12        }13    }It turns out there is a way to accomplish the same thing using direct access to Atmega8 hardware ports and bitwise operators:
1void setup()2    {3        // set pins 1 (serial transmit) and 2..7 as output,4        // but leave pin 0 (serial receive) as input5        // (otherwise serial port will stop working!) ...6        DDRD = B11111110;  // digital pins 7,6,5,4,3,2,1,01// set pins 8..13 as output...2        DDRB = B00111111;  // digital pins -,-,13,12,11,10,9,81// Turn off digital output pins 2..7 ...2        PORTD &= B00000011;   // turns off 2..7, but leaves pins 0 and 1 alone1// Write simultaneously to pins 8..13...2        PORTB = B00111000;   // turns on 13,12,11; turns off 10,9,83    }This code takes advantage of the fact that the control registers
DDRDDDRBPORTBPORTDGenerally speaking, doing this sort of thing is not a good idea. Why not? Here are a few reasons:
- The code is much more difficult for you to debug and maintain and is a lot harder for other people to understand. It only takes a few microseconds for the processor to execute code, but it might take hours for you to figure out why it isn't working right and fix it! Your time is valuable, right? But the computer's time is very cheap, measured in the cost of the electricity you feed it. Usually, it is much better to write code the most obvious way.
- The code is less portable. If you use digitalRead() and digitalWrite(), it is much easier to write code that will run on all of the Atmel microcontrollers, whereas the control and port registers can be different on each kind of microcontroller.
- It is a lot easier to cause unintentional malfunctions with direct port access. Notice how the line 
 above mentions that it must leave pin 0 as an input pin. Pin 0 is the receive line on the serial port. It would be very easy to accidentally cause your serial port to stop working by changing pin 0 into an output pin! Now that would be very confusing when you suddenly are unable to receive serial data, wouldn't it?DDRD = B11111110;
So you might be saying to yourself, great, why would I ever want to use this stuff then? Here are some of the positive aspects of direct port access:
- If you are running low on program memory, you can use these tricks to make your code smaller. It requires a lot fewer bytes of compiled code to simultaneously write a bunch of hardware pins simultaneously via the port registers than it would using a 
 loop to set each pin separately. In some cases, this might make the difference between your program fitting in flash memory or not!for
- Sometimes you might need to set multiple output pins at exactly the same time. Calling 
 followed bydigitalWrite(10,HIGH);
 will cause pin 10 to go HIGH several microseconds before pin 11, which may confuse certain time-sensitive external digital circuits you have hooked up. Alternatively, you could set both pins high at exactly the same moment in time usingdigitalWrite(11,HIGH);PORTB |= B1100;
- You may need to be able to turn pins on and off very quickly, meaning within fractions of a microsecond. If you look at the source code in 
 , you will see that digitalRead() and digitalWrite() are each about a dozen or so lines of code, which get compiled into quite a few machine instructions. Each machine instruction requires one clock cycle at 16MHz, which can add up in time-sensitive applications. Direct port access can do the same job in a lot fewer clock cycles.lib/targets/arduino/wiring.c
More advanced example: disabling an interrupt
Now let's take what we have learned and start to make sense of some of the weird things you will sometimes see advanced programmers do in their code. For example, what does it mean when someone does the following?
1// Disable the interrupt.2      GICR &= ~(1 << INT0);This is an actual code sample from the Arduino 0007 runtime library, in the file
lib\targets\arduino\winterrupts.c1#define INT0   6or
1#define INT0   0So on some processors, the above line of code will compile to:
1GICR &= ~(1 << 0);and on others, it will compile to:
1GICR &= ~(1 << 6);Let us study the latter case, as it is more illustrative. First of all, the value
(1 << 6)~1GICR = GICR & B10111111;This has the effect of leaving all the bits alone in GICR, except for the second-to-highest bit, which is turned off.
In the case where INT0 has been defined to 0 for your particular microcontroller, the line of code would instead be interpreted as:
1GICR = GICR & B11111110;which turns off the lowest bit in the GICR register but leaves the other bits as they were. This is an example of how the Arduino environment can support a wide variety of microcontrollers with a single line of runtime library source code.
Saving memory by packing multiple data items in a single byte
There are many situations where you have a lot of data values, each of which can be either true or false. An example of this is if you are building your own LED grid and you want to display symbols on the grid by turning individual LEDs on or off. An example of a 5x7 bitmap for the letter X might look like this:

A simple way to store such an image is using an array of integers. The code for this approach might look like this:
1const prog_uint8_t BitMap[5][7] = {   // store in program memory to save RAM2        {1,1,0,0,0,1,1},3        {0,0,1,0,1,0,0},4        {0,0,0,1,0,0,0},5        {0,0,1,0,1,0,0},6        {1,1,0,0,0,1,1}7    };1void DisplayBitMap()2    {3        for (byte x=0; x<5; ++x) {4            for (byte y=0; y<7; ++y) {5                byte data = pgm_read_byte (&BitMap[x][y]);   // fetch data from program memory6                if (data) {7                    // turn on the LED at location (x,y)8                } else {9                    // turn off the LED at location (x,y)10                }11            }12        }13    }If this were the only bitmap you had in your program, this would be a simple and effective solution to the problem. We are using 1 byte of program memory (of which there are about 7K available in the Atmega8) for each pixel in our bitmap, for a total of 35 bytes. This is not so bad, but what if you wanted a bitmap for each of the 96 printable characters in the ASCII character set? This would consume 96*35 = 3360 bytes, which would leave a lot less flash memory for holding your program code.
There is a much more efficient way to store a bitmap. Let us replace the 2-dimensional array above with a 1-dimensional array of bytes. Each byte contains 8 bits, and we will use the lowest 7 bits of each to represent the 7 pixels in a column of our 5x7 bitmap:
1const prog_uint8_t BitMap[5] = {   // store in program memory to save RAM2        B1100011,3        B0010100,4        B0001000,5        B0010100,6        B11000117    };(Here we are using the predefined binary constants available starting in Arduino 0007.) This allows us to use 5 bytes for each bitmap instead of 35. But how do we make use of this more compact data format? Here is the answer: we rewrite the function DisplayBitMap() to access the individual bits in each byte of the BitMap...
1void DisplayBitMap()2    {3        for (byte x=0; x<5; ++x) {4            byte data = pgm_read_byte (&BitMap[x]);   // fetch data from program memory5            for (byte y=0; y<7; ++y) {6                if (data & (1<<y)) {7                    // turn on the LED at location (x,y)8                } else {9                    // turn off the LED at location (x,y)10                }11            }12        }13    }The crucial line to understand is
1if (data & (1<<y)) {The expression
(1<<y)datadata & (1<<y)ifelseQuick Reference
In this quick reference, we refer to the bits in a 16-bit integer starting with the least significant bit as bit 0, and the most significant bit (the sign bit if the integer is signed) as bit 15, as illustrated in this diagram:
Whenever you see the variable
n1y = (x >> n) & 1;    // n=0..15.  stores nth bit of x in y.  y becomes 0 or 1.1x &= ~(1 << n);      // forces nth bit of x to be 0.  all other bits left alone.1x &= (1<<(n+1))-1;   // leaves alone the lowest n bits of x; all higher bits set to 0.1x |= (1 << n);       // forces nth bit of x to be 1.  all other bits left alone.1x ^= (1 << n);       // toggles nth bit of x.  all other bits left alone.1x = ~x;              // toggles ALL the bits in x.Here is an interesting function that uses both bitwise
&&&xIsPowerOfTwo(64)trueIsPowerOfTwo(65)false100000010000000111111&00000001000001 & 1000000 == 10000001bool IsPowerOfTwo (long x)2    {3        return (x > 0) && (x & (x-1) == 0);4    }Here is a function that counts how many bits in the 16-bit integer
x1int CountSetBits (int x)2    {3        int count = 0;4        for (int n=0; n<16; ++n) {5            if (x & (1<<n)) {6                ++count;7            }8        }9        return count;10    }Another way is this:
1int CountSetBits (int x)2    {3        unsigned int count;4        for (count = 0; x; count++)5            x &= x - 1;6        return count;7    }Various tricks for common bit-oriented operations can be found here.
Suggest changes
The content on docs.arduino.cc is facilitated through a public GitHub repository. If you see anything wrong, you can edit this page here.
License
The Arduino documentation is licensed under the Creative Commons Attribution-Share Alike 4.0 license.
