Binary and Hexadecimal Representation
Every value inside a computer — an instruction, a pixel, a character, a memory address — is ultimately a pattern of two-state signals. A CPU has no notion of "the number seven." It only has wires that are high or low voltage. To bridge how humans think about numbers with how hardware stores them, we need something more. We need a positional number system built on exactly two symbols. We also need a compact human-readable shorthand. This shorthand lets us write long strings of those symbols without losing our minds. This article develops binary (base-2) as the native representation of digital hardware. It also covers hexadecimal (base-16), the standard notation programmers use. Hex lets us read and write binary data compactly. The two systems are connected by a beautiful arithmetic coincidence — 16 is a power of 2. This coincidence makes conversion between them almost mechanical. This is unlike conversion between binary and decimal. This is why memory dumps, machine code, IP addresses, color codes, and hash digests are printed in hex rather than decimal. Hex is a lossless, dense stand-in for binary that humans can actually read. The big picture: a positional number system represents a value as a weighted sum of digits. Each digit's weight is a power of the base. Base 10 uses weights . Base 2 uses weights . Base 16 uses weights . Once you can compute in one base, computing in another is easier. It is a matter of changing which weights you use. It also means changing which symbols are legal digits. Everything downstream in computer architecture assumes you are fluent in binary and hex first. This includes Two's Complement Integers for signed values. It includes IEEE 754 Floating Point for reals. It also includes the truth tables of Logic Gates that physically realize arithmetic. By the end of this article you will be able to do several things. You will convert any unsigned integer between decimal, binary, and hexadecimal by hand. You will justify why each conversion algorithm terminates with the correct answer. You will recognize the four-bit-per-hex-digit grouping that makes hex a "binary shorthand." You will also avoid the classic conversion mistakes that trip up beginners.
- Positional systems represent a number as ; binary uses base 2 (digits 0,1), hex uses base 16 (digits 0–9, A–F).
- Decimal-to-binary conversion repeatedly divides by 2 and records remainders; decimal-to-hex does the same with 16.
- Binary-to-hex conversion is direct: group bits into 4s from the right (since ), and convert each group to one hex digit.
- Hex exists purely for human convenience — it is a 4x-denser, lossless alias for binary, not a different hardware representation.
- Both conversion directions run in time in the value , since the number of digits scales with .
- These representations describe unsigned magnitudes only; sign and fractions require extensions covered by two's complement and IEEE 754.
A number's value is base-independent, but its digit string depends entirely on the base — and because , binary and hexadecimal digit strings translate into each other one hex digit at a time, with no arithmetic required.
The idea
A positional number system represents a nonnegative integer as a sequence of digits , each drawn from a fixed alphabet of size (the base or radix), whose value is defined by
Decimal () uses digits . Binary () uses only ; each digit is called a bit (binary digit), and it maps directly onto a two-state physical device — a transistor that is on or off, a capacitor that is charged or not, a magnetic domain polarized one way or the other. This is precisely why binary, not decimal, is the native language of digital circuits: building a reliable device with ten distinguishable voltage levels is far harder than building one that only needs to distinguish "high" from "low."
Hexadecimal () uses sixteen digit symbols: – for the values zero through nine, and the letters – (or lowercase –) for the values ten through fifteen, since standard notation has no single symbol for a "digit" above 9. Hexadecimal is never implemented in hardware directly — no wire has sixteen voltage levels. Its entire purpose is human ergonomics: writing out a 32-bit binary number like `10110100111000010100101011110000` is error-prone to read and transcribe, while its hex equivalent `B4E14AF0` is eight characters, easy to chunk, and (as shown below) trivially recoverable back into the exact same bits. The key structural fact tying the two together is that . Because each hex digit represents a value from 0 to 15, and every such value has a unique 4-bit binary representation, one hex digit always corresponds to exactly four bits — no more, no less. This "nibble" correspondence is what makes binary–hex conversion a table lookup rather than an arithmetic computation, in sharp contrast to binary–decimal conversion, which genuinely requires division and multiplication because 10 is not a power of 2.
How it works
There are three conversions worth mastering: decimal → binary, decimal → hexadecimal, and binary ↔ hexadecimal. The first two share an algorithm ("repeated division"); the third is a direct regrouping.
Repeated division algorithm. To convert a decimal integer into base , repeatedly divide by , recording the remainder at each step; the remainders, read from last to first, form the digit string. ``` ALGORITHM ConvertToBase(N, b) // N: nonnegative decimal integer, b: target base (b >= 2) if N == 0: return "0" // base case: zero is its own representation digits = empty list while N > 0: r = N mod b // remainder is the next least-significant digit digits.append(DigitSymbol(r)) // map 0-9 -> '0'-'9', 10-15 -> 'A'-'F' for hex N = N div b // integer division: strip off that digit's weight reverse(digits) // remainders came out least-significant first return concatenate(digits) ```
Why the remainders are the digits. Write in base as . Then
because every term except is a multiple of . So dividing by once "peels off" as the remainder and leaves exactly the number formed by the remaining, higher-order digits — the same problem, one digit smaller. Repeating this until the quotient reaches 0 recovers every digit, from least significant to most significant, which is why the collected remainders must be reversed at the end.
Binary ↔ hexadecimal via nibble grouping. Because , each hex digit is worth exactly 4 bits. To go binary → hex, pad the bit string on the left with zeros until its length is a multiple of 4, split it into groups of 4 starting from the right, and translate each group independently using the 4-bit table below. To go hex → binary, reverse the process: replace each hex digit with its fixed 4-bit pattern and concatenate. ``` ALGORITHM BinaryToHex(bits) // bits: string of '0'/'1', most significant bit first pad_len = (4 - length(bits) mod 4) mod 4 bits = "0" * pad_len + bits // left-pad to a multiple of 4 groups = split bits into chunks of 4, left to right result = "" for each group g in groups: value = binary_value(g) // 0..15, computed from the 4 bits directly result = result + HexSymbol(value) return result ALGORITHM HexToBinary(hexstr) result = "" for each character c in hexstr: v = HexDigitValue(c) // '0'-'9','A'-'F' -> 0..15 result = result + FourBitBinary(v) // fixed 4-bit pattern, no arithmetic return result ``` The 4-bit lookup table used by both directions: ``` Hex Binary Decimal 0 0000 0 1 0001 1 2 0010 2 3 0011 3 4 0100 4 5 0101 5 6 0110 6 7 0111 7 8 1000 8 9 1001 9 A 1010 10 B 1011 11 C 1100 12 D 1101 13 E 1110 14 F 1111 15 ```
Why grouping works, precisely. Split an -bit binary string (padded to a multiple of 4) into groups of 4 bits each, so bit position within the whole number lives in group at local position . The value of the whole number is
where is exactly the 4-bit value of group , and . This is precisely the definition of written in base 16 with digits . So grouping bits by 4 and converting each group independently is not an approximation or a shortcut with edge cases — it is a direct algebraic identity, which is why no carrying or borrowing across groups is ever needed.
Complexity & correctness
Time complexity of repeated division. Converting a value to base via `ConvertToBase` performs one division per output digit, and a number has digits in base . Each division/modulo step on fixed-width machine integers is under the standard RAM model (or bit-level cost if you insist on counting bit operations for arbitrary-precision integers). So the total work is
divisions, i.e., logarithmic in the value being converted, linear in the number of digits produced. This is optimal: you cannot emit fewer than symbols and still uniquely represent , by a simple counting argument — distinct digit strings of length can represent at most distinct values, so representing values up to requires .
Time complexity of binary–hex grouping. This algorithm makes a single linear pass over the bit string (or hex string), doing table-lookup work per group/digit. With bits (equivalently hex digits), the cost is
again linear in the input/output length and asymptotically identical to repeated division — but with a much smaller constant factor and no actual arithmetic, since it is pure regrouping.
Space complexity. Both algorithms use space to store the output digit string (and auxiliary space beyond that, aside from the final reversal in `ConvertToBase`, which can be done in place).
Correctness argument for repeated division (invariant + induction). Claim: at the start of each loop iteration, the current value of `N` equals where is the number of digits already extracted and are the true base- digits of the original input. Base case: before any iterations, and the claim holds trivially (`N` is the original input). Inductive step: given the invariant holds with remaining value , we showed above that and , which is exactly the invariant with incremented by 1. The loop terminates because `N` strictly decreases (it is divided by each time) and is a nonnegative integer, so it must reach 0 in finitely many () steps. On termination, every digit has been extracted in order ; reversing recovers the standard most-significant-first order.
Correctness argument for nibble grouping. This was already given algebraically above: splitting an -bit string into 4-bit groups and reading each as a value satisfies by definition of positional notation in base 16, since . No induction is even needed — it is a direct rewriting of one summation as a nested summation, exploiting that 4 divides evenly into the exponent structure. This is also precisely why grouping by 3 bits works for octal (base 8 = ), and why no such clean grouping exists for decimal, since is not a power of .
Honest tradeoffs. Repeated division is universal — it works for converting to any base, including ones with no power-of-two relationship to binary (like decimal) — but it requires genuine arithmetic (division, modulo) at every step. Nibble grouping is essentially free computationally (bit shifts and masks in hardware, or table lookups by hand) but only works because is a power of ; you cannot use this shortcut to go directly from binary to decimal.
Worked example
Convert (decimal) to binary and to hexadecimal, then cross-check the two answers against each other via nibble grouping.
Step 1 — decimal to binary via repeated division by 2:
| (bit, LSB first) | ||
|---|---|---|
| 46305 | 1 | 23152 |
| 23152 | 0 | 11576 |
| 11576 | 0 | 5788 |
| 5788 | 0 | 2894 |
| 2894 | 0 | 1447 |
| 1447 | 1 | 723 |
| 723 | 1 | 361 |
| 361 | 1 | 180 |
| 180 | 0 | 90 |
| 90 | 0 | 45 |
| 45 | 1 | 22 |
| 22 | 0 | 11 |
| 11 | 1 | 5 |
| 5 | 1 | 2 |
| 2 | 0 | 1 |
| 1 | 1 | 0 |
Reading the bit column from bottom to top gives:
Step 2 — decimal to hexadecimal via repeated division by 16:
| hex digit | |||
|---|---|---|---|
| 46305 | 1 | 1 | 2894 |
| 2894 | 14 | E | 180 |
| 180 | 4 | 4 | 11 |
| 11 | 11 | B | 0 |
Reading bottom to top: .
Step 3 — cross-check via nibble grouping (no arithmetic). Take the binary answer from Step 1 and split into 4-bit groups from the right: ``` 1011 0100 1110 0001 B 4 E 1 ``` This matches Step 2's independently-computed answer exactly, — confirming both algorithms agree, as the correctness proofs guarantee they must, since they are two different routes computing the same base-16 representation of the same number.
Step 4 — verify by expansion.
The arithmetic closes the loop back to the original decimal value, confirming correctness end to end.
Common pitfalls & traps
| Pitfall | Why it happens | Fix |
|---|---|---|
| Forgetting to reverse remainders | Repeated division naturally produces digits least-significant-first | Always reverse the collected remainder list before reading it as the final digit string |
| Padding to the wrong nibble boundary | Binary strings from real programs may not have length divisible by 4 | Left-pad with zeros to the next multiple of 4 before grouping, never right-pad |
| Mixing up hex letter case or value | Confusing A=10 with A=1, or writing digit 10 as "10" instead of "A" | Memorize the 0–15 to 0–F table cold; a hex digit is always exactly one character |
| Treating base conversion as changing the value | Learners sometimes think is "wrong" or a "different number" | The value is base-independent; only the digit string changes — always keep a subscript on ambiguous numerals |
| Assuming hex-to-decimal needs bit grouping | Grouping only helps between binary and a power-of-2 base like hex/octal | Convert hex to decimal via , or route through binary as an intermediate if that's easier |
Pioneers & history
The binary numeral system predates electronic computing by centuries as a mathematical curiosity, but its marriage to physical hardware — and the parallel invention of compact notations like hexadecimal to manage it — is a distinctly 20th-century computer-architecture story.
Origins · 1 minGottfried Wilhelm Leibniz and how this was discovered
Leibniz saw binary as a philosophical and mathematical elegance — a system built from the "nothing" and the "something" — publishing his systematic treatment of binary arithmetic in 1703, roughly two and a half centuries before it became the working substrate of digital computers.
Shannon's 1937 master's thesis proved that networks of relays and switches could implement any Boolean function, meaning binary logic was not just a numbering convention but the literal design language of digital hardware — a result some consider the most consequential master's thesis of the century.
Where this matters
Every layer of a real computing system leans on the binary/hex duality developed here. Machine instructions are stored as raw binary in memory, but assemblers, disassemblers, and debuggers print them in hex because a 32-bit instruction word is a readable 8-character hex string instead of an error-prone 32-character bit string. Memory addresses in a debugger (`0x7ffee23a1050`), color values in CSS and image formats (`#FF5733`), MAC addresses, cryptographic hash digests (SHA-256 output shown as 64 hex characters), and IPv6 addresses are all hex encodings of underlying binary data, chosen specifically because of the clean 4-bit-per-hex-digit correspondence proven above. Inside the CPU itself, every arithmetic and logic unit operates purely on bits — there is no hexadecimal circuitry anywhere in silicon. Understanding binary representation is the prerequisite for Logic Gates, which physically implement addition, comparison, and Boolean operations on those bit patterns. It is also the prerequisite for Two's Complement Integers, which extend unsigned binary to represent negative numbers using the same bit patterns reinterpreted, and for IEEE 754 Floating Point, which further subdivides a binary word into sign, exponent, and mantissa fields to represent real numbers. Compilers, linkers, and file-format specifications (ELF headers, PNG chunks, network packet headers) are routinely documented in hex precisely because it is the most legible lossless view onto the binary reality that hardware actually manipulates.
Ask the tutor: How does the same bit pattern get reinterpreted to represent negative numbers under two's complement?
Why do computers use binary instead of decimal internally?
Binary maps naturally onto physical two-state devices: a transistor that is on or off, a voltage that is high or low, a magnetic domain polarized one way or another. Reliably building and distinguishing ten different voltage levels (for decimal) inside a chip is far harder and noisier than distinguishing just two. Claude Shannon's 1937 thesis formalized this by showing Boolean algebra — a two-valued logic — exactly describes switching circuit behavior, making binary the natural mathematical language for digital hardware. Every other base used in computing, including hexadecimal and octal, is layered on top as a notation for humans, not a change to the underlying physical representation.
Is hexadecimal ever actually stored or computed on directly in hardware?
No. Hardware registers, buses, and memory cells are entirely binary; there is no physical hex ALU or hex memory cell. Hexadecimal is purely a display and input convention used by humans (and by tools like debuggers, disassemblers, and hex editors) because it compresses long bit strings into a shorter, easier-to-read form. When you type a hex literal like `0x2A` into source code, the compiler converts it to the equivalent binary pattern before it ever reaches the hardware; hex never "exists" as a distinct electrical representation.
How many bits does one hexadecimal digit always represent?
Exactly 4 bits, no more and no less, because and every value from 0 to 15 has a unique 4-bit binary pattern. This is a fixed, universal correspondence — not an approximation — which is why converting between binary and hex never requires carrying, borrowing, or any arithmetic: you simply split the bit string into groups of 4 and translate each group using a fixed lookup table, or run the process in reverse.
Why can't you use the same grouping trick to convert binary directly to decimal?
The grouping trick relies on the target base being a power of 2 — specifically, that some fixed number of bits (4, for hex; 3, for octal) exactly represents one digit of the target base with no leftover fractional bit-weight. Decimal's base, 10, is not a power of 2, so there is no fixed number of bits that cleanly corresponds to "one decimal digit"; the place values simply do not line up with binary place values . Converting binary to decimal therefore requires genuine positional-value arithmetic (multiplying each bit by its power of 2 and summing), not regrouping.
What is the difference between an unsigned representation and the signed representations covered later?
Everything in this article describes unsigned integers: every bit pattern represents a nonnegative magnitude, and an -bit pattern can represent values from to . Real programs also need negative numbers, which requires reserving part of the representation's information to encode sign — the dominant scheme in modern hardware is Two's Complement Integers, which reinterprets the same bit patterns so that roughly half represent negative values, while keeping addition circuitry identical to the unsigned case. Binary and hex notation themselves do not change; only how a fixed-width bit pattern is interpreted changes.
Why is hexadecimal specifically preferred over, say, octal (base 8) in modern practice?
Octal (base 8 = ) has the same clean regrouping property as hex, using 3-bit groups, and was popular on early machines with word sizes divisible by 3 (like 12-bit or 36-bit systems). Modern hardware standardized on byte-oriented, power-of-two word sizes (8, 16, 32, 64 bits), all of which divide evenly by 4 but not always as neatly by 3 in a way that aligns with byte boundaries — a byte is exactly two hex digits, but not a whole number of octal digits. Hexadecimal's alignment with the ubiquitous 8-bit byte made it the practical winner.
What happens if I try to convert a number to a base greater than 16, like base 36?
The repeated-division algorithm generalizes without change — divide by , take remainders, map values 0–35 to digit symbols (conventionally 0–9 then A–Z), and reverse at the end. Bases up to 36 are common in applications like URL shorteners and compact ID encodings because they use the full alphanumeric alphabet. The grouping shortcut, however, only works when converting to or from a base that is itself a power of 2 (like hex or octal) relative to binary; base 36 is not a power of 2, so no bit-grouping shortcut exists there, and repeated division is the only correct method.