Binary, octal and hexadecimal numbers

How a computer counts with zeros and ones: binary numbers on LEDs, bits, nibbles and bytes, octal and hexadecimal numbers, and writing them in code.

A row of eight LEDs, of which the first, second and fifth are lit, with a switch under each one: the binary number 11001000

Only two states

An LED can be on or off. A button is pressed or it isn’t. An Arduino pin is HIGH or LOW. Each time there are only two states, and we can write them as 1 and 0.

A computer, a micro:bit and an Arduino store and calculate everything using only zeros and ones: numbers, letters, pictures and sounds. Numbers written with only zeros and ones are called binary numbers.

How do you count with zeros and ones?

We count with ten digits, from 0 to 9. When we run out of digits, we start a new place: after 9 comes 10. Each place is worth ten times more than the one to its right: ones, tens, hundreds…

In binary there are only two digits, 0 and 1, so a new place starts already after 1. Each place is worth twice as much as the one to its right:

Eight equal boxes side by side with the values of the places: 128, 64, 32, 16, 8, 4, 2 and 1

You get the number by adding up the values of the places that have a 1. The binary number 1101 is 8 + 4 + 1 = 13.

NumberBinaryHow it adds up
00
111
2102
3112 + 1
41004
51014 + 1
61104 + 2
71114 + 2 + 1
810008

Where are binary numbers used?

  • In every computer and mobile phone. Pictures, songs, games and messages are stored as strings of bits. Every dot on the screen (pixel) has its own colour, and the colour is stored in a few bytes.
  • In wires and radio links. Zeros and ones travel one after another through a USB cable, over Wi-Fi and over the micro:bit's radio. How this works is explained on the page How devices talk.
  • QR codes. Every dark square is a 1 and every light one a 0. A QR code is a picture made of bits, which a phone reads with its camera.
  • Braille. A character of the writing for blind people has six places for dots, and each dot is raised or not. That is six bits, so 64 different characters are possible.
  • On an Arduino. When a program calls pinMode() or digitalWrite(), one bit changes inside the chip. Each pin has its own bit that says whether it is an input or an output, and its own bit that says whether it is HIGH or LOW.

Try it: numbers on cards

Make five cards. Draw 16 dots on the first, 8 on the second, then 4, 2 and 1. Lay them in a row, with 16 on the left.

Five cards with 16, 8, 4, 2 and 1 dots. Cards 8, 4 and 1 are face up and 16 and 2 face down, so it is written 0 1 1 0 1, which is 13

  • A card with its dots facing up is 1, and one turned over is 0.
  • The number is the sum of the dots on the cards you can see.
  • Try to make 13: you turn over cards 8, 4 and 1, so it is written 01101.
  • Make the number of your age. What is the largest number you can make with five cards?

You can do the same with five LEDs in one row of the micro:bit’s display: a lit LED is 1, and an unlit one 0. The whole micro:bit display has 25 LEDs, so it can show 25 zeros and ones at once.

A micro:bit with the second, third and fifth LEDs of the middle row lit. Above the columns are the values 16, 8, 4, 2 and 1, and next to the row 0 1 1 0 1 = 13

The largest number with five cards is 16 + 8 + 4 + 2 + 1 = 31. With six cards (you add 32) you can make numbers up to 63. Each new card doubles how many numbers you can make.

Bits, nibbles and bytes

One binary digit, one card or one LED, is called a bit. Eight bits together are called a byte. One byte can store the numbers from 0 to 255, because 128 + 64 + 32 + 16 + 8 + 4 + 2 + 1 = 255.

Four bits, half a byte, are called a nibble. One nibble can store the numbers from 0 to 15, because 8 + 4 + 2 + 1 = 15. A byte has two nibbles: the left four bits and the right four bits.

A bite and a nibble

The word byte sounds just like bite. When half a byte needed a name, someone thought of nibble, a small bite. People liked the name, and it stuck.

Memory is measured in bytes. An Arduino Uno has 2 kB (2048 bytes) of memory for variables. A mobile phone has millions of times more.

Octal and hexadecimal numbers

From here on, this page is for the Arduino workshop.

Number systems

A way of writing numbers is called a number system. The number of digits a system uses is called its base, and it tells you what each place is worth: each place is worth base times as much as the one to its right.

SystemBaseDigitsPlace values200 in this system
decimal100 to 9… 1000, 100, 10, 1200
binary20 and 1… 128, 64, 32, 16, 8, 4, 2, 111001000
octal80 to 7… 512, 64, 8, 1310
hexadecimal160 to 9 and A to F… 4096, 256, 16, 1C8

The same number, 200, looks different in each system, but it is always the same number. Checking the octal one: 3 · 64 + 1 · 8 + 0 · 1 = 200. And the hexadecimal one: C is 12, so 12 · 16 + 8 = 200.

The first seventeen numbers in all four systems:

DecimalBinaryOctalHexadecimal
0000
1111
21022
31133
410044
510155
611066
711177
81000108
91001119
10101012A
11101113B
12110014C
13110115D
14111016E
15111117F
16100002010

Why do programmers use octal and hexadecimal numbers at all? Because you can turn binary into them without any arithmetic: you just split the bits into groups.

Hexadecimal numbers

The hexadecimal system has 16 digits: 0 to 9 and A to F (A is 10, B is 11, and so on up to F, which is 15). One hexadecimal digit is exactly four bits, one nibble. So a byte is always two hexadecimal digits, one for each nibble.

Converting is simple: split the binary number into nibbles, starting from the right, and replace each nibble with one digit from the table above. The number 200 is 1100 1000 in binary: the nibble 1100 is C and the nibble 1000 is 8, so it is C8.

The byte 11001000 in eight boxes. Above, the bits are split into two nibbles, 1100 and 1000, which give the hexadecimal digits C and 8. Below, they are split into groups of three, 11, 001 and 000, which give the octal digits 3, 1 and 0

A long string of zeros and ones is easy to misread, and two hexadecimal digits are not. That is why hexadecimal is the way programmers most often write bytes.

Where are hexadecimal numbers used?

  • Colours on the web. The orange of this website is #ff8000. Each colour is one byte: red ff (255), green 80 (128) and blue 00 (0).
  • Device addresses on an I2C connection. The chip that drives the Maqueen's motors has the address 0x10 (see the page How devices talk).
  • Network addresses. Every device on a network, from a mobile phone to a Wi-Fi module, has its own MAC address of six bytes, written as twelve hexadecimal digits, for example 3C:71:BF:4A:12:9E.
  • Letters in Unicode. Every letter of every language in the world has its own number, written in hexadecimal: the Croatian letter Č is U+010C and Ž is U+017D.
  • The contents of files. Programs that show the bytes of a file show them in hexadecimal. Every PNG picture on this website starts with the bytes 89 50 4E 47, and the last three of them are the letters P, N and G.
  • Memory addresses. When a program on a computer crashes, the error message often shows the hexadecimal address of the place in memory where it happened.

Octal numbers

The octal system has eight digits, from 0 to 7. One octal digit is exactly three bits, so binary is turned into octal in groups of three bits, starting from the right. The number 200 is 11 001 000 in binary, which is 3, 1 and 0: 310 in octal (see the picture above).

A byte of eight bits doesn’t split neatly into groups of three, so octal numbers are rarely used today. In the C++ language they hide in one trap, described in the next section.

Where are octal numbers used?

  • File permissions on Linux and the Mac. The command chmod 755 says who may do what with a file. The first digit is for the owner, the second for the owner's group and the third for everyone else. Each digit is three bits: read (4), write (2) and run (1). So 7 = 4 + 2 + 1 means everything, and 5 = 4 + 1 means read and run, but not write.
  • Transponders in aircraft. Every aircraft sends air traffic control a code of four digits from 0 to 7, which the pilot sets on the device. There are 8 · 8 · 8 · 8 = 4096 such codes. Some are special: 7700 means an emergency, and 7600 that the radio isn't working.
  • Old computers. Computers from the 1960s, such as the PDP-8, worked with 12 bits at a time. Twelve bits split into four groups of three, so programmers wrote those numbers in octal.

Numbers in an Arduino program

Writing numbers in code

In a C++ program, what is written in front of a number decides its system:

byte a = 200;          // decimal
byte b = 0b11001000;   // binary: 0b in front
byte c = 0310;         // octal: 0 in front
byte d = 0xC8;         // hexadecimal: 0x in front

All four variables have the same value, 200. In older Arduino examples you will also see the binary form B11001000. That only works in Arduino, so it is better to use 0b.

A zero at the start of a number

C++ reads a number that starts with a zero as octal. So 010 in a program is eight, not ten. If you write a time or a pin with a leading zero to line things up, like 07 or 09, you get the wrong number or an error: 09 doesn't compile, because 9 is not an octal digit. So never put a zero in front of a decimal number.

Printing in the serial monitor

A program can print a number in any of these systems. This program prints the numbers from 0 to 16 in all four systems in the serial monitor, like the table above:

void setup() {
  Serial.begin(9600);
  Serial.println("DEC\tBIN\tOCT\tHEX");
  for (byte n = 0; n <= 16; n++) {
    Serial.print(n);           // decimal
    Serial.print('\t');        // tab: space up to the next column
    Serial.print(n, BIN);      // binary
    Serial.print('\t');
    Serial.print(n, OCT);      // octal
    Serial.print('\t');
    Serial.println(n, HEX);    // hexadecimal
  }
}

void loop() {
}

The output doesn’t start with zeros: 5 is printed as 101, not as 00000101.

A picture made of bits

An 8 × 8 LED matrix has eight rows, with eight LEDs in each row. So one row is exactly one byte: each bit says whether one LED is on. The whole picture is eight bytes:

byte smiley[8] = {
  0b00111100,
  0b01000010,
  0b10100101,
  0b10000001,
  0b10100101,
  0b10011001,
  0b01000010,
  0b00111100
};

An 8 by 8 LED matrix showing a smiley, with each row's byte next to it, from 0b00111100 to 0b00111100
If you look at the ones, you will see a face. You can draw arrays like this with the mouse in the Character generator tool, which turns them into code. The same array can also be written in hexadecimal, more briefly: the first row, 0b00111100, is 0x3C.

The function bitRead(number, place) reads one bit of a number. Places are counted from 0, from the right: bitRead(0b00000100, 2) gives 1.

How many bits does a variable have?

Each type of variable has a fixed number of bits, and so a largest number it can store:

TypeBitsRange on an Arduino UnoLargest number in hexadecimal
byte80 to 2550xFF
int16−32,768 to 32,7670x7FFF
unsigned long320 to 4,294,967,2950xFFFFFFFF

In hexadecimal you can see at once how many bits a number has: each F is four ones.

When a number goes past the largest value, it starts again from the beginning, like the mileage counter in an old car: a byte holding 255 holds 0 after + 1. That is why we store the time from millis() in an unsigned long (see the lesson Two traffic lights at a crossroads).

analogRead() returns a number from 0 to 1023 because of bits too: the converter in the Arduino has 10 bits, and 10 bits can store 1024 different numbers.

What did we learn?

  • Binary numbers have only two digits, 0 and 1. Each place is worth twice as much as the one to its right: 1, 2, 4, 8, 16…
  • One binary digit is a bit, four bits are a nibble, and eight bits are a byte, which stores the numbers from 0 to 255.
  • The octal system has the digits 0 to 7, each of them three bits. The hexadecimal system has the digits 0 to F, each of them four bits, one nibble, so a byte is always two hexadecimal digits.
  • In an Arduino program we write a binary number with 0b, a hexadecimal one with 0x, and an octal one with a zero at the start. That is why we never put a zero in front of a decimal number.
  • Each variable has a fixed number of bits. When a number goes past the largest value, it starts again from the beginning.

Short quiz

1. Which number is written in binary as 1010?
8 + 2 = 10.
2. How do you write the number 6 in binary?
110, because 6 = 4 + 2.
3. How many different numbers can you make with five cards (starting from 0)?
32 numbers, from 0 to 31. Each card doubles the number of possibilities: 2 × 2 × 2 × 2 × 2 = 32.
4. How many bits does a nibble have, and how many nibbles are in a byte?
A nibble has four bits, and a byte has two nibbles.
5. Write the binary number 1111 0011 in hexadecimal.
The nibble 1111 is F and the nibble 0011 is 3, so it is F3 (0xF3 in code).
6. What is 0x1F in decimal?
1 · 16 + 15 = 31.
7. A program says int a = 010;. What is a?
Eight. The zero at the start means the number is octal, and 10 in octal is 1 · 8 + 0 = 8.
8. A variable of type byte has the value 255. What does it have after you add 1?
  1. A byte can’t store 256, so the number starts again from the beginning.
9. Why does analogRead() return numbers from 0 to 1023?
The converter in the Arduino has 10 bits, and 10 bits can store 2 to the power of 10, that is 1024 numbers: from 0 to 1023.