CRC 算法核心:多项式、异或与校验位计算

咱们做网络通信的,天天跟比特流打交道。数据在线上跑,难免会串扰、丢位。CRC 就是用来揪出这些错误的。说白了,它就是个除法 – 但不是什么普通的除法,而是用“多项式”做的除法。

多项式 – CRC 的“DNA”

你可能会问:好好一个除法,怎么扯上多项式了?

其实很简单。发送方把数据看成一个大整数,双方约定好一个“除数”(也就是生成多项式)。发送方做除法后,把余数附在数据后面发过去。接收方收到后用同样的除数再做一次除法,如果余数为零,数据大概率没问题。

这个“除数”长啥样?举个例子:CRC-8 的一个常用多项式是 x⁸ + x² + x + 1。在计算机眼里,它就是一串比特位。怎么映射的?

  • x⁸ 代表第 8 位是 1
  • x² 代表第 2 位是 1
  • x¹ 代表第 1 位是 1
  • x⁰ 代表第 0 位是 1

核心转换规则:多项式指数对应比特位置,有这一项就填 1,没有就填 0。

所以 x⁸ + x² + x + 1 转换为二进制就是:1 0000 0111(从 x⁸ 到 x⁰,共 9 位)。

我个人习惯把多项式看成“战斗模式”。不同的多项式擅长抓不同类型的错误。比如 CRC-32 在以太网里用得最多,因为它对突发错误的检测能力很强。

异或运算 – CRC 的“基石”

CRC 里面没有进位,没有借位。你只需要一个操作:异或(XOR)。

异或的规则很简单:相同为 0,不同为 1。

输入 A输入 B输出 (A XOR B)
000
011
101
110

为什么只能用异或?因为多项式除法本质上就是模 2 除法。加法不进位,减法不借位,异或正好满足这个要求。

你看硬件实现的时候,就是一排异或门。我在项目里见过有人看不懂 LFSR(线性反馈移位寄存器),其实它就是个异或门的组合,一层层往回反馈而已。

校验位计算 – 动手算一遍

光说理论太虚了。我们来算一个实际的例子。

假设:

  • 原始数据: 1101 (二进制)
  • 生成多项式: 1011 (对应 x³ + x + 1)
  • CRC 宽度: 3 位(因为多项式最高次是 x³)

步骤分解

  1. 数据补零: 在原始数据后面补上 CRC 宽度个 0。这里补 3 个 0 → 1101 000。
  2. 开始除法: 用补零后的数据对多项式 1011 做模 2 除法(只用异或)。
  3. 找到余数: 做完整除法后,余数就是 CRC 校验值。

我们手动模拟一下过程:

          _______________
1011 )  1 1 0 1 0 0 0
        1 0 1 1         ← 第一次对齐,异或
        ------ 
          0 1 1 0 0 0   ← 落下一位
            1 0 1 1     ← 第二次对齐,异或
            -----
              0 1 1 1 0 ← 落下一位
                1 0 1 1 ← 第三次对齐,异或
                -----
                  0 1 0 1 ← 余数只有 3 位?
                            不,最高位是 x²,所以补成 3 位:101

余数位数不够怎么办? 左侧补 0。上面余数 101 位于 x² 位,本身已经是 3 位,不需要补。

如果余数是 01,则要补成 001。

所以,发送方发出去的数据是:1101 101(原始数据 + 余数)。

接收方收到后,会用 1101 101 去除以 1011。如果余数还是 0,数据就是干净的。

常见 CRC 多项式一览

不同的协议用不同的多项式。我整理了一个表,你可以对照看看:

名称多项式二进制表示常见应用
CRC-8x⁸ + x² + x + 11 0000 01111-Wire 总线
CRC-16-IBMx¹⁶ + x¹⁵ + x² + 11 1000 0000 0000 0101Modbus
CRC-32x³² + x²⁶ + x²³ + x²² + x¹⁶ + x¹² + x¹¹ + x¹⁰ + x⁸ + x⁷ + x⁵ + x⁴ + x² + x + 11000 0010 0110 0000 1000 1110 1101 1011Ethernet, ZIP

避坑: 多项式选择不是随便来的。宽多项式不一定比窄的强。我见过有人为了省资源选了个窄多项式,结果误码率高的吓人。选择多项式要依赖于你信道的错误模型。

一句话总结核心逻辑

CRC 校验核心流程 原始数据 M M 左移 n 位 模 2 除法 余数 R 发送帧:M + R 信道传输 收到帧:M' + R' 接收方用同一多项式 对 M'+R' 做除法 余数 = 0 ? 正确 : 错误

嗯,整个 CRC 的核心逻辑就这么简单。发送方算个余数附在后面,接收方再算一次看余数是否为 0。没有复杂的数学,就是移位加异或。

我个人建议:刚开始接触时别急着看硬件实现,先在纸上算几笔,把“多项式等于二进制”这个映射刻在脑子里。后面做协议分析时你会发现,这东西比打表好理解多了。