CRC、汉明码与游程编码:检错、纠错和压缩的适用范围
通信、存储和数据处理里经常把“编码”统称为一类技术,但目标其实不同:有些方法用于发现错误,有些能够定位并修复有限的错误,还有些只是在利用数据冗余进行压缩。区分各方法的适用范围,才能避免把不具备纠错能力的 CRC 当成恢复手段,或把不适合高熵数据的游程编码用于错误场景。
三类问题,三种工具
| 方法 | 主要目的 | 能力范围 | 典型场景 |
|---|---|---|---|
| CRC | 检错 | 通常不能定位或纠正错误 | 网络帧、存储块、固件完整性 |
| 汉明码 | 单比特纠错 | 基础形式不能可靠处理任意多比特错误 | ECC 存储、教学与简单传输接口 |
| RLE/RLC | 无损压缩 | 只在长重复序列上有效 | 位图、掩码、重复值序列 |
CRC:用多项式余数检错
CRC(循环冗余校验)把一串比特视为 GF(2) 上的多项式。发送端选择生成多项式 G(x),在数据末尾补零后做模 2 除法;除法中的减法就是 XOR,得到的余数附在数据尾部。
以数据 11010011101100 和生成多项式 1011 为例:生成多项式的阶数为 3,因此数据末尾先补 3 个零,模 2 除法的余数为 100,最终发送码字为:
11010011101100 100
接收端用同一生成多项式重新相除。余数为零表示“未检测到错误”,不是数学上绝对保证没有错误;检错能力取决于多项式、帧长和错误模式。工程中常见 CRC-8、CRC-16、CRC-32,它们的多项式、初始值、输入/输出反射和最终异或值必须完全一致,不能只凭位数互通。
实现时容易遗漏的参数
- 多项式的正常表示与反射表示;
- 初始寄存器值(init);
- 输入与输出是否 bit-reflect;
- 最终异或值(xorout);
- 字节序与覆盖范围,是否包含长度、头部或填充。
调试 CRC 不一致时,应先使用一组公开测试向量逐项比对这些参数,再检查硬件移位方向和软件表驱动实现。
汉明码:利用 syndrome 定位单比特错误
经典 (7,4) 汉明码用 4 个数据位和 3 个校验位组成 7 位码字。校验位放在 1、2、4 等 2 的幂次位置:
位置: 1 2 3 4 5 6 7
内容: p1 p2 d1 p4 d2 d3 d4
以偶校验为例,每个校验位覆盖一组位置;接收端重新计算得到 s1、s2、s4。把 s4 s2 s1 当作二进制数,非零值就是检测到的单比特错误位置。例如 101 表示第 5 位出错。
基础汉明码能纠正单比特错误,但无法可靠地区分“单错”与部分双错情形。实际 ECC 场景常加入一个整体校验位,形成 SECDED(single-error correction, double-error detection):单比特错误可纠正,双比特错误可检测但不可纠正。
RLE:把重复值换成运行段
RLE(Run-Length Encoding,也常写作 RLC)不处理传输错误,而是把连续重复的值表示为 (value, count):
原始:0000001111000
编码:(0, 6)(1, 4)(0, 3)
在程序内存中,运行段通常是结构体数组或两个并行数组,而不是人眼看到的文本:
values = [0, 1, 0]
counts = [6, 4, 3]
它适合大片纯色、稀疏掩码和重复值较多的数据;对高熵数据,运行段元数据可能让结果更大。实现时还要确定 count 的位宽、单段最大长度、结构体对齐,以及当数据恰好含有控制值时的转义规则。
选择与组合
三者可以在同一传输路径中出现,但不能互相替代。例如,图像数据可先进行无损压缩,再对最终数据包附加 CRC;带 ECC 的存储器在读出时先尝试纠正单比特错误,再把更高层的校验交给 CRC 或哈希完成。
设计时最重要的问题是:系统需要的是压缩、发现错误、恢复错误,还是确认数据端到端完整性?先明确目标,再决定校验强度、编码冗余和软件/硬件实现位置。