Binary and Hexadecimal Notation: A refresher for Computer Programming

Binary and hexadecimal notation are used everywhere. Let's brushen up on the topic if you're already familiar. For people that are new to it let me know if there are places that need revision for clarity. Pros, let me know where I'm wrong. Binary notation: Binary notation represents decimal numbers as bits or ones and zeros. In binary each digit is a bit which can be either 1 or 0. This is the number 1 represented as binary: 0001. This is the number 2 represented as binary: 0010. There are four bit places in the example above. That makes them 4-bit binary numbers. The number is read from the rightmost place first or the least significant bit: 0101 ↑ first digit (least significant bit) in a binary number 2s complement: All the bits of any given binary number together represent a number in a system called 2s complement, where each bit is a representation of 2^nth power : The rightmost digit (lsb) is either 0 or 2^0 power. The second from the rightmost digit is either 0 or 2^1 power. The third from the rightmost digit is either 2^2 power. The fourth from the rightmost digit is either 2^3 power. And so on... 0 0 0 0 (lsb) 2^3 2^2 2^1 2^0 0 or 8 0 or 4 0 or 2 0 or 1 0010 equals 2 as a decimal value. 2s complement is a system for representing numbers in binary format. Each bit is a power of two starting with 0 at the very rightmost digit. 8 (2^3) 4 (2^2) 2 (2^1) 1 (2^0) binary result as integer 0 0 0 1 0001 1 0 0 1 0 0010 2 0 0 1 1 0011 3 0 1 0 0 0100 4 Using 2s complement binary notation, any positive integer can be expressed. I'm not going into negative numbers for now - let's keep it simple. You can figure out any binary number manually with simple addition: 1101 ↑ First bit = 1 1101 ↑ Second bit = 0 1101 ↑ Third bit = 4 1101 ↑ Fourth bit = 8 1 + 4 + 8 = 13 1101 = 13 Number of bits in binary notation To be explicit, when we talk about bits, this is what is meant: 4-bit binary notation: 0000 8-bit binary notation: 0000 0000 16-bit binary notation: 0000 0000 0000 0000 Binary arithmetic Basic binary arithmetic like addition and subtraction may not impress you. However, Binary numbers can be used to perform some mathematical wizardry, as you will see. Binary addition In binary addition two overlapping positive bits will result in a carry over. See 1 + 1: 0001 (1) + 0001 (1) ------ = 0010 (2) Binary subtraction Binary subtraction is straightforward, simply subtract overlapping bits. 0001 (1) - 0001 (1) ------ = 0000 (0) Binary Logic: AND and OR Here's where binary numbers get really fun. Logical operations like AND and OR ect on binary numbers are just like a truth table, but flipped on its side like a math(s) operation. Just line them up vertically like a math problem: Logical AND AND: 1111 (15) & 0011 (3) ------ = 0011 (3) Even more spectacular: AND operations can act as what's called bit masking because only the bits from the "masking" number will remain. For example: binary AND any number with 3 (as we did above 0011), and only the lower two bits will remain. This can be useful for checking when a bit is set: bool isBitOneOrTwoSet; uint8_t bitMask = 3; // 0011 as binary uint8_t number = // some value if(number & bitMask){ isBitOneOrTwoSet = true; } Challenge: Can you think of a good way to write a function that tells you the bit index of the highest bit set in a binary number? Common operations with bit masking: 0xFF // Mask for a byte (8 bits) 0xFFFF // Mask for a word (16 bits) 0x0F // Mask for lower 4 bits 0xF0 // Mask for upper 4 bits 0x01 // Mask for least significant bit 0x80 // Mask for most significant bit Logical OR OR is taking a back seat to AND in this write up. It works the same way as you would expect. OR: 0001 (1) | 0010 (2) ------ = 0011 (3) What's most impressive to me about OR is that at first glance it just looks like addition, but remember it's an OR operation not addition. Take 1 and 1 for example, its result is 1: 0001 (1) | 0001 (1) ------ = 0001 (1) This is a bit "hand wavy", but it may be used in situations where writing to a row of pixels. Suppose an application needs to write to a display. Here is our display. It's a 32w x 9h grid of bits: 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 each pixel in the display is either on or off pixels are written to in blocks of 16-bit chunks (first row of pixels is two 16-bit chunks) a

Apr 27, 2025 - 00:49
 0
Binary and Hexadecimal Notation: A refresher for Computer Programming

Binary and hexadecimal notation are used everywhere. Let's brushen up on the topic if you're already familiar. For people that are new to it let me know if there are places that need revision for clarity. Pros, let me know where I'm wrong.

Binary notation:

Binary notation represents decimal numbers as bits or ones and zeros. In binary each digit is a bit which can be either 1 or 0.

This is the number 1 represented as binary: 0001.

This is the number 2 represented as binary: 0010.

There are four bit places in the example above. That makes them 4-bit binary numbers. The number is read from the rightmost place first or the least significant bit:

0101
   ↑ first digit (least significant bit) in a binary number

2s complement:

All the bits of any given binary number together represent a number in a system called 2s complement, where each bit is a representation of 2^nth power :

  • The rightmost digit (lsb) is either 0 or 2^0 power.
  • The second from the rightmost digit is either 0 or 2^1 power.
  • The third from the rightmost digit is either 2^2 power.
  • The fourth from the rightmost digit is either 2^3 power.
  • And so on...
0 0 0 0 (lsb)
2^3 2^2 2^1 2^0
0 or 8 0 or 4 0 or 2 0 or 1

0010 equals 2 as a decimal value.

2s complement is a system for representing numbers in binary format. Each bit is a power of two starting with 0 at the very rightmost digit.

8 (2^3) 4 (2^2) 2 (2^1) 1 (2^0) binary result as integer
0 0 0 1 0001 1
0 0 1 0 0010 2
0 0 1 1 0011 3
0 1 0 0 0100 4

Using 2s complement binary notation, any positive integer can be expressed. I'm not going into negative numbers for now - let's keep it simple. You can figure out any binary number manually with simple addition:

1101
   ↑ First bit  = 1

1101
  ↑  Second bit = 0

1101
 ↑   Third bit  = 4

1101
↑    Fourth bit = 8

1 + 4 + 8 = 13
1101      = 13

Number of bits in binary notation

To be explicit, when we talk about bits, this is what is meant:

4-bit binary notation:

0000

8-bit binary notation:

0000 0000

16-bit binary notation:

0000 0000 0000 0000

Binary arithmetic

Basic binary arithmetic like addition and subtraction may not impress you. However, Binary numbers can be used to perform some mathematical wizardry, as you will see.

Binary addition

In binary addition two overlapping positive bits will result in a carry over. See 1 + 1:

    0001    (1)
  + 0001    (1)
  ------
  = 0010    (2)
Binary subtraction

Binary subtraction is straightforward, simply subtract overlapping bits.

    0001    (1)
  - 0001    (1)
  ------
  = 0000    (0)

Binary Logic: AND and OR

Here's where binary numbers get really fun. Logical operations like AND and OR ect on binary numbers are just like a truth table, but flipped on its side like a math(s) operation. Just line them up vertically like a math problem:

Logical AND

AND:

    1111    (15)
  & 0011    (3)
  ------
  = 0011    (3)

Even more spectacular:

AND operations can act as what's called bit masking because only the bits from the "masking" number will remain. For example: binary AND any number with 3 (as we did above 0011), and only the lower two bits will remain.

This can be useful for checking when a bit is set:

bool isBitOneOrTwoSet;
uint8_t bitMask = 3; // 0011 as binary
uint8_t number = // some value

if(number & bitMask){
    isBitOneOrTwoSet = true;
}

Challenge: Can you think of a good way to write a function that tells you the bit index of the highest bit set in a binary number?

Common operations with bit masking:

0xFF       // Mask for a byte (8 bits)
0xFFFF     // Mask for a word (16 bits)
0x0F       // Mask for lower 4 bits
0xF0       // Mask for upper 4 bits
0x01       // Mask for least significant bit
0x80       // Mask for most significant bit
Logical OR

OR is taking a back seat to AND in this write up. It works the same way as you would expect.

OR:

    0001    (1)
  | 0010    (2)
  ------
  = 0011    (3)

What's most impressive to me about OR is that at first glance it just looks like addition, but remember it's an OR operation not addition. Take 1 and 1 for example, its result is 1:

    0001    (1)
  | 0001    (1)
  ------
  = 0001    (1)

This is a bit "hand wavy", but it may be used in situations where writing to a row of pixels. Suppose an application needs to write to a display. Here is our display. It's a 32w x 9h grid of bits:

00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
  • each pixel in the display is either on or off
  • pixels are written to in blocks of 16-bit chunks (first row of pixels is two 16-bit chunks)
  • a pixel is on when the bit related to the pixel is on

In the example above if a row looks like this:

00000000000000011100000000000000

Then pixels 17, 18, and 19 are on. To write to other pixels in the row the dev might use bitwise or:

    11100000000000000
  | 00000000000001110
  -------------------
  = 11100000000001110

You can imagine how this might be helpful in something like: function drawPixel(x, y);

Challenge: Create your own api for a screen of pixels. What would the drawPixel implementation look like?

Hexadecimal Notation

Hexadecimal notation offers a more compact way of viewing and expressing binary numbers. It lets us represent numbers above 9 using a single character.

hexadecimal number decimal
0 0
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
A 10
B 11
C 12
D 13
E 14
F 15

A hexadecimal number can be recognized because it always begins with 0x: 0x0

Hexadecimal Anatomy:

  • Prefix (0x) - indicates hexadecimal formatting of the number
  • Digits - values that come after 0x range from 0 to F
  • Places - each digit after the 0x represents a nibble of 4 bits expressed from left to right

hexadecimal numbers range from 0 to F:

hex binary integer
0x0 0000 0
0x1 0001 1
0x2 0010 2
0x3 0011 3
0x4 0100 4
0x5 0101 5
0x6 0110 6
0x7 0111 7
0x8 1000 8
0x9 1001 9
0xA 1010 10
0xB 1011 11
0xC 1100 12
0xD 1101 13
0xE 1110 14
0xF 1111 15

Hexadecimal Concatenation

One of the benefits of hexadecimal notation is the way values can be stacked or concatenated to create higher bit representations:

hex # of bits binary integer
0x1 4 0001 1
0x11 8 0001 0001 16 + 1 = 17
0x111 12 0001 0001 0001 128 + 16 + 1 = 145
0x1111 16 0001 0001 0001 0001 4096 + 128 + 16 + 1 = 4241

0x11 is the same as 0001 0001 which when expressed as integer is 17. So a hexadecimal number is a concatenation expression of each nibble of 4 bits from left to right - pretty cool right!? This can be expressed as a formula.

In a two-digit hexadecimal number, the first digit can be thought of as A and the second digit as B. The hex value can be calculated with the formula: A * (2^n number of bits) + B:

0xFF
  ↑ Digit A
0xFF
   ↑ Digit B
0xFF <-- two digits, making this a concatenation of two 4-bit numbers

The above makes the equation: F x 2^4 + F or 15 x 16 + 15 = 255.

hex binary formula mathematical representation integer
0xFF concat 1111 and 1111 A x (2^4) + B 15 x 16 + 15 255
0xEF concat 1110 and 1111 A x (2^4) + B 14 x 16 + 15 239
0xEFEF concat 1110 1111 and 1110 1111 A x (2^8) + B 239 x 256 + 239 61423
  • A four-digit hexadecimal number can be thought of as concatenating two 8-bit hexadecimal values.

What about 12-bit concatenation or other bit lengths?

To concatenate any number of bits, simplify the problem by adding each hexadecimal digit's value individually. Take the value 0xBCE for instance:

0xBCE = 0xB00 + 0x0C0 + 0x00E

To represent this as binary, we left shift the hexadecimal value by 4 bits for each hexadecimal place:

   1011 0000 0000  (0xB00)  = 11 x 16^2
 + 0000 1100 0000  (0x0C0)  = 12 x 16^1
 + 0000 0000 1110  (0x00E)  = 14 x 16^0
 ----------------
 = 1011 1100 1110  (0xBCE)  = (11x16^2) + (12x16^1) + (14x16^0)

The above is called bit shifting. In C, the expression below is equivalent to what we did above:

// shifts 'B' 8 bits to the left
// shifts 'C' 4 bits to the left
// Logical OR concatenates `B`, `C` and E!