Loading learning content...
The sender has computed the CRC and transmitted the protected codeword. The bits have traversed noisy cables, potentially suffering interference, attenuation, and corruption. Now it's the receiver's turn: did the data arrive intact, or has it been corrupted in transit?
CRC verification answers this question with elegant simplicity. The same polynomial division that created the CRC now validates it. If the data survived transmission unchanged, a mathematical property guarantees a specific result. If any bit flipped, that property is violated—and the error is detected.
This page explores CRC verification in depth: the two equivalent verification methods, why they work, how errors manifest as non-zero syndromes, and practical considerations for real-world implementations.
By the end of this page, you will understand the two methods for CRC verification (recompute-and-compare vs. divide-and-check-remainder), why correct codewords always produce zero remainder, how errors produce non-zero syndromes, what happens when errors go undetected, and how real systems handle CRC failures.
The receiver obtains a string of bits—let's call it T'(x). This is supposedly the transmitted codeword T(x), but transmission errors may have corrupted it:
$$T'(x) = T(x) + E(x)$$
where E(x) is the error polynomial (zeros everywhere except at corrupted bit positions).
The Receiver's Challenge:
The receiver knows:
The receiver does NOT know:
The Goal:
Determine whether T'(x) is a valid codeword. If so, extract M(x). If not, signal an error.
CRC can only determine if data is 'valid' (divisible by G(x)), not whether it's 'correct' (equals what was sent). An undetected error occurs when E(x) is divisible by G(x)—the corrupted data is still 'valid' but incorrect. This is rare for well-chosen generators but theoretically possible.
The most intuitive verification approach mirrors the sender's process:
Algorithm:
Separate the codeword into message M'(x) (first n bits) and received CRC R'(x) (last r bits)
Recompute the CRC from M'(x) using the same algorithm the sender used, obtaining R_computed(x)
Compare: If R'(x) = R_computed(x), accept the message. Otherwise, reject.
Why This Works:
If no errors occurred, then:
If errors occurred in the message:
If errors occurred only in the CRC:
Worked Example:
Received: T' = 1101011 (7 bits: 4-bit message + 3-bit CRC) Generator: G = 1011
Step 1: Separate
Step 2: Recompute CRC of M' = 1101 Append 3 zeros: 1101000 Divide by 1011:
1101000 ÷ 1011:
1101000
1011
────
0110000 → 110000
1011
────
011100 → 11100
1011
────
01010 → 1010
1011
────
0001 → 001
R_computed = 001
Step 3: Compare R' = 011 ≠ R_computed = 001
Result: Error detected! The received CRC doesn't match what the message should produce.
This method explicitly yields the expected CRC, which can be useful for debugging. If you know the received CRC is wrong, comparing with the computed CRC can reveal the error pattern. This is occasionally useful in protocol analysis.
A more elegant approach leverages a key property: valid codewords are exactly divisible by G(x).
Algorithm:
Divide T'(x) by G(x) (the entire received codeword, including the CRC portion)
Check the remainder:
Why This Works:
Recall that the transmitted codeword was constructed as: $$T(x) = x^r \cdot M(x) + R(x)$$
where R(x) = (x^r · M(x)) mod G(x).
This means T(x) mod G(x) = 0 by construction.
If T'(x) = T(x) (no errors): $$T'(x) \mod G(x) = T(x) \mod G(x) = 0 \quad ✓$$
If T'(x) = T(x) + E(x) (errors occurred): $$T'(x) \mod G(x) = (T(x) + E(x)) \mod G(x) = 0 + E(x) \mod G(x) = E(x) \mod G(x)$$
This is non-zero unless G(x) divides E(x)—which is extremely rare for well-chosen G(x).
Worked Example:
Case A: No Errors
Received: T' = 1101001 (the correct codeword from earlier) Generator: G = 1011
Divide T' by G:
1101001 ÷ 1011:
1101001
1011
────
0110001 → 110001
1011
────
011101 → 11101
1011
────
01011 → 1011
1011
────
0000
Remainder = 000 ✓
Zero remainder → No error detected
Case B: One Bit Error
Original T = 1101001, but bit 4 flipped → T' = 1111001
Divide T' by G:
1111001 ÷ 1011:
1111001
1011
────
0100001 → 100001
1011
────
(align at pos 5, but 100001 < 1011 at high positions...)
Actually: 1111001
1011
────
01000|01 → 100001
(leading 1 at position 5)
1011 (shift to position 5-2)
────
01100|1 → 110001
1011
────
01110|1 → 111001...
Let me compute more carefully:
1111001
Step 1: 1111 vs 1011 at pos [6:3] → 1111 XOR 1011 = 0100
Result: 0100001 → 100001
Step 2: 1000 vs 1011 at pos [5:2] → cannot (1000 < 1011 as 4-bit? No, 1000 > 1011 is false)
Actually 1000 starts with 1, so we can XOR:
1000 XOR 1011 = 0011
Result: 0011|01 → 11001 (bring down)
Step 3: Wait, let me track positions...
Let me use the shift register method instead for clarity:
Input: 1111001 (bits from left to right) Register (3-bit): 000 Poly: 1011 → feedback pattern 011
Process bit by bit:
Final: 110 ≠ 000
Non-zero remainder → Error detected!
Method 2 is computationally simpler: just one division operation, checking only if the remainder is zero. No need to separate the codeword or perform comparisons. This is why most hardware and software implementations use the divide-and-check-remainder approach.
When the remainder of T'(x) ÷ G(x) is non-zero, that remainder is called the syndrome. The syndrome contains valuable information about the error that occurred.
Definition:
$$\text{Syndrome } S(x) = T'(x) \mod G(x) = E(x) \mod G(x)$$
The syndrome is entirely determined by the error polynomial—it doesn't depend on the original message! This is because:
$$S(x) = (T(x) + E(x)) \mod G(x) = \underbrace{T(x) \mod G(x)}_{= 0} + E(x) \mod G(x) = E(x) \mod G(x)$$
Key Properties:
S(x) = 0 implies no detectable error (E(x) = 0 or E(x) is divisible by G(x))
S(x) ≠ 0 implies an error was detected
Same syndrome, same error class: Different error patterns with the same syndrome are indistinguishable by CRC
| Error Pattern E(x) | Binary | E(x) mod G(x) | Syndrome |
|---|---|---|---|
| No error | 0000000 | 000 | 000 |
| Bit 0 error | 0000001 | 001 | 001 |
| Bit 1 error | 0000010 | 010 | 010 |
| Bit 2 error | 0000100 | 100 | 100 |
| Bit 3 error | 0001000 | 011 | 011 |
| Bit 4 error | 0010000 | 110 | 110 |
| Bit 5 error | 0100000 | 111 | 111 |
| Bit 6 error | 1000000 | 101 | 101 |
| Bits 0,1 error | 0000011 | 011 | 011 |
Observations from the Table:
Each single-bit error has a unique syndrome for positions 0-5 (up to 2^r - 1 positions for a degree-r CRC)
Bit 3 error and double error (bits 0,1) have the same syndrome (011) — The CRC detects that an error occurred but cannot distinguish these cases
No single-bit error has syndrome 000 — All single-bit errors are detected
Error Correction Potential:
In some applications (not standard CRC), the syndrome can be used to locate single-bit errors:
However, this only works for single-bit errors within a limited message length. CRC is fundamentally designed for error detection, not correction.
Different error patterns can produce the same syndrome (like bit 3 error and bits 0+1 error above). This doesn't mean those errors are undetected—both produce non-zero syndrome. It means CRC can't distinguish between those specific error patterns. For detection purposes, this doesn't matter: any non-zero syndrome triggers retransmission.
CRC is not perfect. An error goes undetected when the error polynomial E(x) is exactly divisible by G(x):
$$E(x) = Q(x) \cdot G(x) \quad \text{for some polynomial } Q(x)$$
In this case, the syndrome S(x) = E(x) mod G(x) = 0, and the corrupted data is accepted as valid.
What Does an Undetected Error Look Like?
For G(x) = x³ + x + 1 = 1011, some divisible error patterns are:
These are specific, structured error patterns—not random bit flips. The probability of random noise producing exactly such a pattern is vanishingly small.
Probability Analysis:
For an r-bit CRC with random errors:
Single-bit error: Probability of being undetected = 0 (guaranteed detection if g₀ = 1)
Double-bit error: Probability of being undetected ≈ 0 (detected if message length < 2^r - 1)
Burst of length ≤ r: Probability of being undetected = 0 (guaranteed)
Burst of length r + 1: Probability of being undetected = 2^(-(r-1))
Random pattern > r bits: Probability of being undetected ≈ 2^(-r)
In Practice:
With CRC-32, roughly 1 in 4 billion random error patterns goes undetected. Combined with:
The probability of encountering an undetected corrupted frame is approximately:
$$10^{-5} \times 2.3 \times 10^{-10} \approx 2 \times 10^{-15}$$
At 10 Gbps, this is roughly one undetected corrupted frame per century of continuous transmission.
For network communications, CRC provides sufficient protection for data integrity at the frame level. Higher layers (TCP checksums, application-level verification) add additional protection. For critical applications like financial transactions or medical devices, cryptographic hashes (SHA-256) provide stronger guarantees against both random errors and malicious tampering.
Real systems must handle both CRC pass and CRC fail cases gracefully. Let's examine the practical considerations.
On CRC Pass (Syndrome = 0):
On CRC Fail (Syndrome ≠ 0):
The appropriate action depends on the protocol and layer:
Option A: Silent Discard (Most Common at Link Layer)
Option B: Request Retransmission (Data Link ARQ)
Option C: Forward Error Correction
| Protocol | On CRC Fail | Retransmission Mechanism |
|---|---|---|
| Ethernet | Silent discard | TCP/upper layer handles loss |
| WiFi (802.11) | Silent discard | MAC-layer retries (up to 7) |
| HDLC | NAK or timeout | Go-Back-N or Selective Repeat |
| USB | NAK | Hardware retransmit (3 tries) |
| PCIe | NAK | Replay from buffer |
| TCP | Silent discard | Timeout and retransmit segment |
Statistics and Monitoring:
Well-designed systems track CRC failures for diagnostics:
Example: Ethernet Interface Statistics
$ ifconfig eth0
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>
RX errors 0 dropped 0 overruns 0 frame 0
TX errors 0 dropped 0 overruns 0 carrier 0
The 'errors' counter includes CRC failures. In healthy networks, this should stay at or near zero.
Most network interfaces strip the CRC before delivering frames to software. The OS never sees the CRC bytes—only the CRC pass/fail result (typically as an error counter or flag). This saves memory bandwidth and simplifies software processing.
Efficient CRC verification is crucial for high-speed links. Several optimizations apply:
1. Incremental Verification
Rather than buffering the entire frame and then verifying, compute the CRC as data arrives. The shift-register approach naturally supports this:
2. The Magic Residue
For many CRC standards, if you process the entire codeword (including the CRC bytes) through the CRC engine, you get a constant non-zero value—the magic residue or check value:
This occurs because of the final XOR (complement) in the standard. Checking for this constant is equivalent to checking for zero remainder in the 'pure' algorithm.
3. Hardware CRC Engines
Network interface cards (NICs) perform CRC in hardware:
4. Parallel CRC
For very high-speed links (100 Gbps+), serial bit processing is too slow. Solutions include:
These techniques achieve CRC verification at line rate even for terabit networks.
If implementing CRC verification, determine the expected residue for your standard. Don't assume it's zero—that's only true for the 'pure' algorithm without initialization and final XOR. For CRC-32 Ethernet, after processing the entire frame including the FCS bytes, the register should equal 0xC704DD7B.
We've explored how receivers use CRC to determine whether received data is error-free. Let's consolidate the key insights:
What's Next:
With CRC calculation and verification mastered, we conclude with a tour of common CRC standards—the specific polynomials, parameters, and applications of CRC-8, CRC-16, CRC-32, and CRC-64 in real-world protocols and systems.
You now understand both sides of the CRC handshake: sender calculation and receiver verification. The elegant property that valid codewords are exactly divisible by the generator—and that any bit flip disrupts this divisibility—enables reliable error detection across billions of devices worldwide. Next, we survey the CRC standards that power global communications.