The register holds r bits
· algorithms
The bit at x^r is congruent to the rest of the generator. The register keeps r bits and XORs that rest when the old top bit was 1.
Last time the shift made one bit past degree , and a single XOR with cleared it. That extra bit is , and the leading coefficient of is , so their sum is . I know the result before I store the bit.
The bit that cancels itself
Take , so , and a remainder with the next bit . This is the first of the four zeros after . Last time’s step shifts, sees bit , and XORs the whole generator:
10110^ 10011------- 00101Split off the top bit. The generator is with :
1 0110^ 1 0011-------- 0 0101The top line is . The register only has to keep the low bits and, when that cancelled bit was going to be , XOR .
Whether the shift produces is decided by the MSB already in the register. The register is bits wide, so
may be . It is the coefficient of in that fixed width, which is what msb reads. The leading coefficient of is the highest power that is actually present, and that power may sit lower than . One shift moves the coefficient of onto .
Replace the leading power
Since ,
and moving across the congruence changes its sign:
In that minus sign does nothing. The addition table says , so , and the same holds coefficient by coefficient: . Therefore
For this generator, and then . The two sides differ by :
10000^00011------10011So the update never builds the bit at . The incoming bit is . Reducing modulo drops every power of degree or more, and that is a different operation from . With this same and ,
If , then is already . If , add :
On the same step, because starts with . The shift’s low four bits are , and
0110^0011-----0101Same remainder as the five-bit XOR.
The program drops the leading of the generator with a mask, appends the zero bits as before, and keeps the register inside those bits:
// Fixed-width register. `truncated_generator` is `generator` without its// leading 1, which already sits at bit `width`.[[nodiscard]] uint32_t register_crc(const std::vector<int>& message_bits, uint32_t generator) { const int width = polynomial_degree(generator); uint32_t crc_register = 0; std::vector<int> augmented = message_bits; augmented.resize(augmented.size() + static_cast<size_t>(width), 0); const uint32_t register_mask = (1U << width) - 1; const uint32_t truncated_generator = generator & register_mask;
for (const int bit : augmented) { const uint32_t msb = (crc_register >> (width - 1)) & 1U; crc_register = ((crc_register << 1) | static_cast<uint32_t>(bit)) & register_mask; if (msb == 1) { crc_register ^= truncated_generator; } }
return crc_register;}msb is . truncated_generator is . (crc_register << 1) & register_mask, with the new bit in the low place, is . The mask is what makes true at every step, not only after a reduction. The value is still of the bits read so far, so the message followed by four zeros still ends at .
A generator of degree is bits long, since the coefficient of is . The familiar constant 0x04C11DB7 is that polynomial with removed: it is , which is all the register stores.
The zeros are still written out at the end of the message. The next step is to fold each message bit into the top of the register and stop appending them.