← Back to blogNumber systems

BCD (Binary-Coded Decimal): Fundamentals Explained

BCD encodes each decimal digit as 4 bits. Learn packed and unpacked forms, the +6 addition correction, decimal scaling, and practical trade-offs.

DennyDenny
9 min read
Chalkboard feature illustration for “BCD (Binary-Coded Decimal): Fundamentals Explained”

TL;DR: Binary-coded decimal (BCD) stores each decimal digit 0–9 in a 4-bit nibble, leaving 1010–1111 unused. A BCD digit adder applies a +6 correction when the raw binary sum exceeds 9 or produces carry-out. BCD preserves stored decimal digits exactly, but finite-precision division and scaling still require explicit rounding rules.

Binary and decimal use different prime factors in their place values, so many finite decimal fractions—including 0.1—have no finite binary expansion. Binary-coded decimal stores decimal digits directly. That avoids an initial binary-fraction representation error, but a sound application must still define scale, precision, and rounding for operations whose result has more digits than the format can hold.

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:

DecimalBCD nibble
00000
10001
20010
30011
40100
50101
60110
70111
81000
91001

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:

32710  =  0011⏟3  0010⏟2  0111⏟7327_{10} \;=\; \underbrace{0011}_{3}\;\underbrace{0010}_{2}\;\underbrace{0111}_{7}

Compare that with the pure-binary representation 32710=1010001112327_{10}=101000111_2, which uses 9 bits instead of 12. BCD spends extra bits to preserve a digit-oriented representation and simplify some decimal interfaces.

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.

FormatDigit 327BytesDensity
Unpacked0x03 0x02 0x0731 digit/byte
Packed0x03 0x2722 digits/byte
Pure binary (16-bit)0x01 0x472full 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 stored decimal digits. The decimal 0.1 has no terminating binary representation. BCD can store 0.1 exactly when the decimal-point position is part of the format. Arithmetic is exact only while the mathematical result fits the chosen digit count and scale; division and other non-terminating results still require rounding. IEEE 754 floating point covers the binary side of this trade-off.

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.

Decimal business rules. Financial and metering applications often specify decimal scales and rounding. Packed decimal, COBOL COMP-3, decimal floating point, and scaled binary integers are all viable representations. BCD is useful when digit-level storage or interfaces matter; regulation does not universally mandate BCD.

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:

7=01115=010112=1100\begin{aligned} 7 &= 0111 \\ 5 &= 0101 \\ \hline 12 &= 1100 \end{aligned}

The result 1100 is 12 in binary, which is one of the invalid BCD patterns. Apply the +6 correction:

1100  (12)+  0110  (6)1 0010\begin{aligned} 1100 \;(12) \\ +\;0110 \;(6) \\ \hline 1\,0010 \end{aligned}

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

8=10009=100117=1 0001\begin{aligned} 8 &= 1000 \\ 9 &= 1001 \\ \hline 17 &= 1\,0001 \end{aligned}

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:

0001+  01100111\begin{aligned} 0001 \\ +\;0110 \\ \hline 0111 \end{aligned}

The final result is 0001 0111, which is BCD for 17. Without correction, concatenating the raw carry and low nibble gives 0001 0001, which a BCD decoder reads as 11—not 17.

The general detection condition for “needs correction” is:

adjust  =  Cout  ∨  (S3∧S2)  ∨  (S3∧S1)\text{adjust} \;=\; C_{\text{out}} \;\lor\; (S_3 \land S_2) \;\lor\; (S_3 \land S_1)

where S3S2S1S0S_3 S_2 S_1 S_0 is the 4-bit binary sum. The two AND terms together cover the six invalid patterns 1010–1111, and CoutC_{\text{out}} 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 nn of these blocks and you have an nn-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 using two adder blocks plus the detect logic, then drive a digit display from the result. Keep inputs constrained to valid BCD digits 0–9; the correction expression is not a general sanitizer for invalid input nibbles.

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 A−BA - B:

  1. Form the 9’s complement of BB by replacing each digit dd with 9−d9 - d.
  2. Add the result to AA as a BCD addition.
  3. If a final carry-out occurs, add 1 to the result (10’s complement adjust). The carry indicates a positive result.
  4. 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.

More arithmetic work. Each digit add needs a binary add, correction detection, and correction. Several legacy x86 decimal-adjust instructions are unavailable in 64-bit mode. Whether packed decimal, decimal floating point, or scaled integers perform best depends on the ISA, workload, library, and required semantics.

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.
  • Calculators. Digit-oriented arithmetic can simplify decimal entry and display, although calculator implementations also use binary and other internal representations.
  • Industrial control panels. Thumbwheel switches output BCD directly. PLC inputs read BCD without conversion.
  • Enterprise systems. Architectures and languages with packed-decimal support remain useful for fixed-scale business data and COBOL workloads.
  • Meters and point-of-sale equipment. Decimal displays and specified rounding rules can make digit-oriented formats convenient, although implementation choices vary.

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.

  1. Wire two 4-bit input registers AA and BB feeding an adder block. Display the raw 5-bit sum.
  2. Build the detect logic: an OR of CoutC_{\text{out}}, (S3∧S2)(S_3 \land S_2), and (S3∧S1)(S_3 \land S_1).
  3. Use the detect signal to gate a constant 0110 into a second adder, and add it to the first sum.
  4. Drive the lower nibble of the corrected result into a digit display. Drive the BCD carry-out into a separate digit.
  5. Sweep A=0..9A = 0..9 and B=0..9B = 0..9. 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.

Apply the concept in a simulator model.