Number Systems & Base Conversion
Digital hardware only has two stable states to work with per wire, so every number a digital circuit stores or computes has to ultimately be represented in base 2 — binary. Everything in this page is really in service of one skill: fluently moving between how humans prefer to read numbers (decimal, and its shorthand, hexadecimal) and how hardware actually stores them (binary).
Positional number systems
Every number system covered here is positional — the value of a digit depends on where it sits, not just what it is. In decimal, 243 means 2×10² + 4×10¹ + 3×10⁰. The same idea generalizes to any base r: a digit d at position i (counting from 0 at the rightmost digit) contributes d × r^i to the total value.
| Base | Name | Digits used | Common use in digital design |
|---|---|---|---|
| 2 | Binary | 0, 1 | What hardware actually stores and computes |
| 8 | Octal | 0–7 | Rare today; historically used to shorten binary (groups of 3 bits) |
| 10 | Decimal | 0–9 | How humans naturally think about quantities |
| 16 | Hexadecimal | 0–9, A–F | Human-readable shorthand for binary (groups of 4 bits) |
Binary
1011₂ = 1×2³ + 0×2² + 1×2¹ + 1×2⁰ = 8 + 0 + 2 + 1 = 11₁₀
Hexadecimal
Hex exists purely for human convenience — each hex digit maps exactly onto 4 binary bits (a nibble), with no rounding or remainder, which is what makes conversion between them trivial in a way conversion to/from decimal never is.
| Hex | Binary | Hex | Binary |
|---|---|---|---|
| 0 | 0000 | 8 | 1000 |
| 1 | 0001 | 9 | 1001 |
| 2 | 0010 | A | 1010 |
| 3 | 0011 | B | 1011 |
| 4 | 0100 | C | 1100 |
| 5 | 0101 | D | 1101 |
| 6 | 0110 | E | 1110 |
| 7 | 0111 | F | 1111 |
1011 1010₂ splits cleanly into two nibbles, 1011 and 1010, giving BA₁₆ directly — no arithmetic required. This is why register dumps, memory addresses, and waveform values in every simulator and debugger are shown in hex: it's binary, just written 4 bits at a time.
This grouping is exactly why Verilog lets you write literals in any base directly — 8'hBA, 8'b10111010, 8'd186 all name the same 8-bit value. You'll meet this formally in Verilog; it's worth recognizing now that the hex form is just the binary form read four bits at a time.
Converting decimal to binary
The standard method is repeated division by 2, reading remainders from bottom to top:
Convert 26 to binary:
26 ÷ 2 = 13 remainder 0
13 ÷ 2 = 6 remainder 1
6 ÷ 2 = 3 remainder 0
3 ÷ 2 = 1 remainder 1
1 ÷ 2 = 0 remainder 1
Reading remainders bottom-to-top: 11010₂
Check: 16 + 8 + 0 + 2 + 0 = 26 ✓
For fractional values, the equivalent technique is repeated multiplication by 2, reading the integer part of each result top to bottom, which converges (or terminates) as digits are generated — many decimal fractions, like 0.1, never terminate in binary at all, which is a real, recurring source of floating-point rounding surprises in software and is worth knowing about even at this level.
Signed and unsigned representation
Everything above assumes an unsigned value — no negative numbers. An n-bit unsigned field represents 0 to 2ⁿ − 1. Representing negative numbers needs a deliberate encoding choice, and digital hardware has settled almost universally on one:
Two's complement
To negate a value in two's complement: invert every bit, then add 1.
0001 0110 (22, unsigned)
Invert:
1110 1001
Add 1:
1110 1010 (-22, two's complement)
Two's complement wins over the alternatives (sign-magnitude, one's complement) for one decisive practical reason: addition hardware doesn't need to know or care whether its operands are signed. The same binary adder circuit produces the correct result whether you feed it two unsigned values or two two's-complement signed values — no separate "subtract" circuit, no special-casing the sign bit. That single property is why virtually every ALU in every processor uses it.
- The most significant bit doubles as the sign bit:
0for non-negative,1for negative — but it's still a normal place-value bit in the arithmetic, not a special flag bit off to the side. - An n-bit two's-complement field represents
-2ⁿ⁻¹to2ⁿ⁻¹ − 1— one more negative value than positive, because there's no redundant "negative zero" the way sign-magnitude has. - Sign extension — widening a signed value to more bits — just replicates the sign bit into the new upper positions.
1010₂(-6, 4-bit) sign-extends to1111 1010₂(-6, 8-bit); the value is unchanged because you're just repeating the same-sign place values that were implicitly there at any width.
Adding two large positive two's-complement numbers can produce a result that "wraps around" into negative territory — the classic case being an n-bit adder whose two positive inputs sum to more than 2ⁿ⁻¹ − 1. Real ALUs detect this (an overflow flag) rather than silently returning a wrong answer; when you design or verify an adder later in this topic and in Verification, overflow behavior is one of the first things worth explicitly testing, not an edge case to discover by accident.
What's next
Representation is only half the picture — the next page covers how binary values are actually added, subtracted, and manipulated by hardware, building directly on the two's-complement representation introduced here.