Notes

CRC is a polynomial remainder

· algorithms

Long division over F_2 leaves a remainder. A CRC is that division after the message shifts left by the generator's degree, which clears room to append the check.

I was learning CRC today. I started from the division I could already do on paper: given D(x)D(x) and G(x)G(x) over F2[x]\mathbb{F}_2[x], compute the remainder of D(x)D(x) on division by G(x)G(x). The CRC is one further shift.

A message may be corrupted on the way out. I append a short check, and the receiver divides the whole word by a generator chosen in advance. That check is the remainder. The place it lives is the end of the message.

Arithmetic in F_2

F2\mathbb{F}_2, also called GF(2)\mathrm{GF}(2), is the field with two elements. Addition and subtraction are both XOR:

++0011
000011
111100

Multiplication is AND:

×\times0011
000000
110011

A word is a polynomial

The bit string 101101101101 is

x5+x3+x2+1.x^{5}+x^{3}+x^{2}+1.

The left bit is the highest power, so a uint32_t is a polynomial in F2[x]\mathbb{F}_2[x]. Likewise 1001110011 is x4+x+1x^{4}+x+1, and its degree is 44: the index of the highest 11. The loop starts at −1-1 and adds one on each right shift until the word is zero.

[[nodiscard]] int polynomial_degree(uint32_t polynomial) {
int degree = -1;
while (polynomial > 0) {
polynomial >>= 1;
++degree;
}
return degree;
}

Long division

While the dividend still has degree at least deg⁡G\deg G, cancel its leading term. If deg⁡D=n\deg D=n and deg⁡G=r\deg G=r, the multiple that lines up is xn−rG(x)x^{n-r}G(x), and divisor << shift is that multiple. XOR is the addition.

[[nodiscard]] uint32_t polynomial_remainder(uint32_t dividend,
uint32_t divisor) {
const int divisor_degree = polynomial_degree(divisor);
while (polynomial_degree(dividend) >= divisor_degree) {
const int shift = polynomial_degree(dividend) - divisor_degree;
dividend = dividend ^ (divisor << shift);
}
return dividend;
}

Take D=1011010000D=1011010000 and G=10011G=10011, so deg⁡D=9\deg D=9 and deg⁡G=4\deg G=4. The first shift is 9−4=59-4=5:

1011010000
^ 1001100000
------------
0010110000

The degree is now 77, so the shift is 33:

0010110000
^ 0010011000
------------
0000101000

The degree is 55, so the shift is 11:

0000101000
^ 0000100110
------------
0000001110

The degree is 33, below 44, and the loop stops. The remainder is 11101110.

Each step replaces DD by D+xkGD+x^{k}G with k=deg⁡D−deg⁡Gk=\deg D-\deg G. That sum is congruent to DD modulo GG, and the two leading terms are the same power, so they cancel and the degree falls. The loop begins with the original dividend, which is congruent to itself, and it ends at a polynomial of degree less than deg⁡G\deg G in the same residue class. That remainder is unique. Write rem⁡G(D)\operatorname{rem}_{G}(D) for it: the polynomial of degree less than r=deg⁡Gr=\deg G congruent to DD modulo GG. The value left in dividend is rem⁡G(D)\operatorname{rem}_{G}(D).

One shift makes it a CRC

The check appended to a message MM is

R(x)=rem⁡G(xrM(x)),r=deg⁡G.R(x)=\operatorname{rem}_{G}(x^{r}M(x)), \qquad r=\deg G.

Multiplying by xrx^{r} shifts the message left by rr and leaves rr zeros at the end. Those zeros are the room for the CRC. For G=10011G=10011, the degree is 44, and the message 101101101101 shifts to 10110100001011010000. That is the dividend above, so

CRC⁡(101101)=1110.\operatorname{CRC}(101101)=1110.
[[nodiscard]] uint32_t crc(uint32_t message, uint32_t generator) {
const int width = polynomial_degree(generator);
return polynomial_remainder(message << width, generator);
}

message << width is xrM(x)x^{r}M(x). Appending the remainder gives the word xrM(x)+R(x)x^{r}M(x)+R(x). The low rr bits were zero, so writing RR there is (message << width) | R. Here that word is 10110111101011011110. Division gives xrM=QG+Rx^{r}M=QG+R, and R+R=0R+R=0, so the word equals QGQG. The generator divides the message with the CRC appended.

C++In F2[x]\mathbb{F}_2[x]
uint32_t pa polynomial PP
polynomial_degree(p)deg⁡P\deg P
p << kxkPx^{k}P
a ^ bA+BA+B
polynomial_remainder(dividend, divisor)rem⁡G(D)\operatorname{rem}_{G}(D)
crc(message, generator)rem⁡G(xrM)\operatorname{rem}_{G}(x^{r}M)

The next step is to divide without building xrMx^{r}M as one integer.