BCD (Binary-Coded Decimal): Fundamentals Explained
TL;DR: Binary-coded decimal (BCD) stores each decimal digit 0–9 in its own 4-bit nibble, leaving codes 1010–1111 invalid. Adding two BCD digits requires a “+6” correction whenever the binary sum exceeds 9. BCD trades storage efficiency for exact decimal arithmetic, which is why it survives in calculators, digital clocks, and financial hardware.
Binary is efficient, but it lies about decimals. The number 0.1 cannot be represented exactly in binary, so a calculator that stores money in floating-point will eventually disagree with the till by a fraction of a cent. Binary-coded decimal sidesteps the problem entirely: instead of converting a decimal value into a single binary number, it encodes each digit on its own and accepts the storage overhead in exchange for arithmetic that always agrees with paper.
What is Binary-Coded Decimal?
BCD represents each decimal digit 0 through 9 using its own 4-bit binary code. Four bits can encode 16 patterns, but BCD uses only the first ten:
| Decimal | BCD nibble |
|---|---|
| 0 | 0000 |
| 1 | 0001 |
| 2 | 0010 |
| 3 | 0011 |
| 4 | 0100 |
| 5 | 0101 |
| 6 | 0110 |
| 7 | 0111 |
| 8 | 1000 |
| 9 | 1001 |
The six remaining patterns 1010 through 1111 — values 10 through 15 in pure binary — are invalid in BCD. A circuit that produces one of these patterns has either overflowed a digit or has been fed a malformed input, and BCD adders must detect this case and correct it.
A multi-digit decimal number is encoded by stringing nibbles together. The number 327, for example, becomes:
Compare that with the pure-binary representation , which uses 9 bits instead of 12. BCD always costs more bits, but the tradeoff buys exact decimal behavior.
Packed vs Unpacked BCD
There are two common storage layouts.
Unpacked BCD stores one decimal digit per byte. The low nibble holds the digit; the high nibble is either zero or carries an ASCII-style tag (0x30 in early IBM and x86 BCD instructions, producing the printable ASCII digit directly). Unpacked BCD wastes half the storage but is trivial to print.
Packed BCD stores two decimal digits per byte, one in each nibble. The number 327 in packed BCD takes two bytes: 0x03 0x27. Packed BCD is twice as dense and is the format used inside hardware BCD adders and the x86 DAA/DAS instructions.
| Format | Digit 327 | Bytes | Density |
|---|---|---|---|
| Unpacked | 0x03 0x02 0x07 | 3 | 1 digit/byte |
| Packed | 0x03 0x27 | 2 | 2 digits/byte |
| Pure binary (16-bit) | 0x01 0x47 | 2 | full range |
For values that fit in 16 bits (up to 65,535), pure binary matches packed BCD on density while encoding 65,536 distinct values rather than 10,000. The cost only reappears when you need to perform arithmetic that must match decimal exactly.
Why BCD Exists at All
If binary is denser, why bother? Three reasons keep BCD alive.
Exact decimal fractions. The decimal 0.1 has no terminating binary representation. In IEEE 754 double precision it becomes 0.1000000000000000055…, and after enough additions the rounding errors compound. BCD has no such problem: each digit is exact, and decimal fractions can be represented and manipulated without loss. A future post on IEEE 754 floating point covers the binary side of this tradeoff in depth.
Trivial display conversion. A digital clock or calculator drives a 7-segment display from each BCD nibble through a small lookup decoder. There is no division by 10, no repeated-subtraction loop, no software conversion. The same advantage motivates the BCD-to-7-segment decoder that we cover in a follow-up post.
Regulatory compliance. Financial systems, fuel pumps, point-of-sale registers, and currency-handling firmware are often required to perform decimal arithmetic to the cent. IBM’s mainframe PACKED DECIMAL type, the COBOL COMP-3, and the IEEE 754-2008 decimal floating-point formats all trace back to the same idea: store digits, not bits, when the digits are what matter.
Adding BCD Digits: the +6 Correction
BCD addition uses ordinary 4-bit binary adders — see the 4-bit ripple-carry adder post for a refresher — followed by a decimal-adjust stage that fixes results larger than 9.
The rule is simple. Add two BCD digits as if they were 4-bit binary numbers. Then:
- If the binary sum is less than or equal to 9 and there was no carry-out, the BCD result is the binary sum unchanged.
- If the binary sum is greater than 9 or there was a carry-out from the 4-bit adder, add 6 (
0110) to correct, and propagate a carry into the next digit.
Why 6? Because BCD skips the six invalid patterns 1010–1111. Any binary sum that lands in or beyond that range is exactly six positions ahead of where it should be in the BCD numbering, so adding 6 wraps it back around and emits the carry that decimal arithmetic expects.
Worked Example: 7 + 5
Encode the operands and add as 4-bit binary:
The result 1100 is 12 in binary, which is one of the invalid BCD patterns. Apply the +6 correction:
The corrected result is 0001 0010, which is BCD for 12 — a carry into the tens place plus a 2 in the units place. Exactly what decimal arithmetic should produce.
Worked Example: 8 + 9
The 4-bit adder produces 0001 with a carry-out. The carry-out triggers the +6 correction even though 0001 looks like a valid BCD digit:
The final result is 0001 0111, which is BCD for 17. Without the correction, the carry would have produced 0001 0001 — sixteen — which is wrong by exactly six.
The general detection condition for “needs correction” is:
where is the 4-bit binary sum. The two AND terms together cover the six invalid patterns 1010–1111, and catches the case where the sum already wrapped past 15.
Hardware: a Single-Digit BCD Adder
A one-digit BCD adder is built from two 4-bit binary adders and a small correction-detect circuit:
A[3:0] B[3:0]
| |
v v
+-----------------+
| 4-bit adder #1 | <-- ordinary binary add (see /docs/ADDER)
+-----------------+
S[3:0] C_out
| |
+--+--+--+
| |
v v
+----------+
| detect | --> adjust = C_out OR (S3·S2) OR (S3·S1)
+----------+
|
v 0110 if adjust, else 0000
+-----------------+
| 4-bit adder #2 | <-- adds the correction
+-----------------+
|
v
BCD result, BCD carry-out
The first adder does the raw binary addition. The detect block computes the adjust signal. The second adder unconditionally adds either 0 or 6 to the intermediate sum. The BCD carry-out is the adjust signal itself: any time we needed to add 6, we are also propagating a 1 into the next decimal digit. Daisy-chain of these blocks and you have an -digit BCD ripple adder. The structure mirrors the binary full adder versus half adder chain, just with a wider digit and a correction stage.
You can build one of these in a simulator using two ADDER blocks plus a couple of gates for the detect logic, then drive a DIGIT_DISPLAY from the result. A two-digit BCD adder will count 00–99 cleanly while a pure binary adder of the same width would skip from 9 straight to A on each digit.
BCD Subtraction
Subtraction follows the same pattern but uses 9’s complement or 10’s complement in place of binary’s 2’s complement. To compute :
- Form the 9’s complement of by replacing each digit with .
- Add the result to as a BCD addition.
- If a final carry-out occurs, add 1 to the result (10’s complement adjust). The carry indicates a positive result.
- If no carry-out, the result is negative; recomplement to recover the magnitude.
The 9’s complement plays the same role for decimal that 1’s complement plays for binary, and 10’s complement is the decimal sibling of 2’s complement. The BCD ALU just needs the same correction stages on the other side of subtraction.
Disadvantages of BCD
The benefits come with real costs.
Storage overhead. Every decimal digit costs 4 bits, so 4 bytes encode at most 99,999,999. The same 32 bits in pure binary represent values up to roughly 4.29 billion — a 43-fold range advantage. For scientific or graphics work, BCD is wasteful.
Slower arithmetic. Each BCD digit add needs a binary add, a detect, and a correction add. On modern superscalar CPUs, the dedicated BCD instructions (AAA, DAA, and family on x86) were either deprecated in 64-bit mode or simply outpaced by software multi-precision binary routines. BCD only wins when an external constraint — display, regulation, hardware peripheral — forces it.
No native fractional support. BCD as described here encodes integers. Fractional BCD requires fixing a decimal point by convention or moving to densely packed decimal or IEEE 754-2008 decimal floats, which encode digits in groups of three using a more compact code. The history of these encodings traces back to the earliest binary number systems and the long argument over whether to count in tens or twos.
Where BCD Still Lives
BCD survives anywhere the inputs and outputs are decimal even if the computation is not.
- Digital clocks and watches. A 24-hour clock stores hours, minutes, and seconds as six BCD digits. A counter chip increments seconds; a small comparator detects 60 and rolls into minutes; the same pattern handles 60 minutes and 24 hours. Each digit drives a 7-segment LED through a BCD-to-7-segment decoder.
- Pocket calculators. Every cheap solar calculator on a desk somewhere is a BCD machine end to end. The keypad produces BCD digits, an internal BCD ALU performs add/subtract/multiply/divide on those digits, and the display reads back BCD without ever touching binary.
- Industrial control panels. Thumbwheel switches output BCD directly. PLC inputs read BCD without conversion.
- Financial mainframes. IBM z/Architecture still has hardware support for packed-decimal arithmetic up to 31 digits, used by COBOL programs that move trillions of dollars per day.
- Smart meters and fuel pumps. Regulatory metrology requires that the displayed number match the stored number to the cent or to the milliliter. BCD makes that audit trivial.
The lesson generalizes. When the problem domain is naturally decimal, encoding the digits explicitly often beats converting between bases on every operation. The same logic argues for fixed-point integer arithmetic in money-handling software today, even when the underlying CPU is happily binary.
Try It in the Simulator
A clean exercise: build a single-digit BCD adder and watch the correction in action.
- Wire two 4-bit input registers and feeding an ADDER block. Display the raw 5-bit sum.
- Build the detect logic: an OR of , , and .
- Use the detect signal to gate a constant 0110 into a second ADDER, and add it to the first sum.
- Drive the lower nibble of the corrected result into a DIGIT_DISPLAY. Drive the BCD carry-out into a separate digit.
- Sweep and . Confirm the display reads 0–18 across two digits without any A–F symbols ever appearing.
For wider values, chain a second BCD adder block. The carry-out of the first becomes the carry-in of the second, just like a binary ripple chain.
What’s Next
A single-digit BCD adder is only useful if you can read it. The next post — BCD to 7-Segment Decoder: From Numbers to Display — derives the segment-driving Boolean expressions from the BCD truth table and shows how to wire the result to a real display, building on the decoder fundamentals we covered earlier. If you want to see the binary side of the same problem, the IEEE 754 floating-point walkthrough makes the case for binary fractions and shows why financial code keeps choosing decimal anyway.
Open the 4-bit binary counter template to feed a counting input into a BCD adder and watch the correction trigger on every tenth tick — the most direct way to see the +6 in action.