数学原理:模2除法、多项式表示法、生成多项式

好,咱们接着聊CRC的核心数学原理。说实话,我第一次接触CRC时,看到那些多项式、模2除法,头都大了。后来在项目里调了三天三夜的通讯,才真正搞明白——原来这东西没那么玄乎。

CRC校验的本质,说白了就是用除法算余数。只不过这个除法,不是咱们小学学的十进制除法,而是模2除法。你想想看,二进制世界里只有0和1,加法不进位,减法不借位,那除法自然也得入乡随俗。

模2除法:没有进位的世界

模2除法,也叫多项式除法。它的规则特别简单:

  • 加减法都是异或运算(XOR),1+1=0,1-1=0
  • 没有进位,没有借位
  • 除法过程中,每一步只看当前最高位是不是1

举个例子,咱们算一下 1101 ÷ 11:

  1001
11)1101
   11
   --
    00
    00
    --
     01
     00
     --
      1  ← 余数

看到了吗?每一步都是异或,不是减法。我在项目中遇到过一位同事,用普通除法去算CRC,结果怎么都对不上。嗯,这就是踩坑了。

核心要点:模2除法 = 按位异或。记住这个,CRC就懂了一半。

多项式表示法:换个马甲而已

多项式表示法,其实就是把二进制数写成多项式的形式。比如:

  • 二进制 1101 → 1·x³ + 1·x² + 0·x¹ + 1·x⁰ = x³ + x² + 1
  • 二进制 1011 → x³ + x + 1

为什么非要这么搞?我个人习惯用多项式来理解CRC,因为生成多项式决定了CRC的检错能力。你想想看,不同的多项式就像不同的锁,有的锁好开,有的锁难撬。

这里有个小技巧:多项式的最高次幂,就是CRC的位数。比如 x³ + x + 1,最高次是3,那就是3位CRC,校验值范围0~7。

生成多项式:CRC的灵魂

生成多项式,就是CRC算法里那个固定的除数。不同的协议标准,用不同的多项式。我整理了一张表,都是工业现场常用的:

CRC类型 生成多项式(二进制) 生成多项式(十六进制) 常见应用
CRC-8 100000111 0x07 1-Wire、SMBus
CRC-16/IBM 1000000000000101 0x8005 Modbus RTU
CRC-16/CCITT 10001000000100001 0x1021 XMODEM、蓝牙
CRC-32 100000100110000010001110110110111 0x04C11DB7 Ethernet、ZIP

我的经验:选生成多项式时,别自己发明。直接用标准协议里定义好的。我曾经见过一个团队自己编了个多项式,结果跟别人的设备死活对不上——通讯协议这东西,标准化才是王道。

CRC计算过程:手算一次就懂了

咱们拿CRC-8举个例子,生成多项式是 0x07(二进制 100000111)。假设要发送的数据是 0x53(二进制 01010011)。

步骤是这样的:

  1. 数据后面补0:CRC-8是8位,所以补8个0。数据变成 01010011 00000000
  2. 用生成多项式做模2除法:100000111 除 0101001100000000
  3. 余数就是CRC校验值

手算过程(简化版):

        10110010  ← 商(我们不关心)
100000111)0101001100000000
          100000111
          ---------
           001000010
            100000111
            ---------
             000001010000
                 100000111
                 ---------
                  000101110  ← 余数 = 0x5C

所以CRC-8校验值是 0x5C。发送时,把数据 0x53 和校验值 0x5C 一起发出去。接收端收到后,用同样的多项式再做一次除法,如果余数为0,说明数据没被篡改。

注意:不同协议对CRC的初始值、结果异或值、输入输出反转等细节有不同规定。比如Modbus RTU的CRC-16,初始值是0xFFFF,结果还要异或0x0000。这些细节在实现时一定要查协议文档,别想当然。

为什么CRC能检错?

这个问题我当年也琢磨了很久。其实道理很简单:

  • 发送端算出的余数,相当于给数据加了个指纹
  • 接收端重新算一遍,如果指纹对不上,说明数据在传输过程中变了
  • 生成多项式选得好,就能检测出单比特错、双比特错、奇数个错、突发错

举个例子,CRC-16能检测出所有长度≤16的突发错误。这意味着什么?你想想看,工业现场电磁干扰再厉害,一般也就连续干扰几个比特。CRC-16基本能全兜住。

一句话总结:CRC就是用模2除法算余数,生成多项式就是除数的二进制表示。选对多项式,CRC就是工业通讯的守护神。

下一章咱们会聊CRC的代码实现,从查表法到直接计算法,我都会给出完整的Python代码。到时候你就能亲手写一个CRC校验函数了。