【A GUIDE TO CRC ERROR DETECTION ALGORITHM】 (译文1)
A GUIDE TO CRC ERROR DETECTION ALGORITHM (译文)
《A PAINLESS GUIDE TO CRC ERROR DETECTION ALGORITHM》 Author : Ross N. Williams
CRC: Cyclic Redundancy Check
Everything you wanted to know about CRC algorithms, but were afraid to ask for fear that errors in your understanding might be detected.
Version : 3.
Date : 19 August 1993.
Author : Ross N. Williams.
Net : ross@guest.adelaide.edu.au.
FTP : ftp.adelaide.edu.au/pub/rocksoft/crc_v3.txt
Company : Rocksoft^tm Pty Ltd.
Snail : 16 Lerwick Avenue, Hazelwood Park 5066, Australia.
Fax : +61 8 373-4911 (c/- Internode Systems Pty Ltd).
Phone : +61 8 379-9217 (10am to 10pm Adelaide Australia time).
Note : "Rocksoft" is a trademark of Rocksoft Pty Ltd, Australia.
Status : Copyright (C) Ross Williams, 1993. However, permission is
granted to make and distribute verbatim copies of this
document provided that this information block and copyright
notice is included. Also, the C code modules included
in this document are fully public domain.
Thanks : Thanks to Jean-loup Gailly (jloup@chorus.fr) and Mark Adler
(me@quest.jpl.nasa.gov) who both proof read this document
and picked out lots of nits as well as some big fat bugs.
摘要
This document explains CRCs (Cyclic Redundancy Codes) and their table-driven implementations in full, precise detail. Much of the literature on CRCs, and in particular on their table-driven implementations, is a little obscure (or at least seems so to me).
本文档详细解释了CRC(循环冗余码)及其表驱动实现。关于CRC的许多文献,特别是关于其表驱动实现的文献,都有点晦涩难懂(或者至少在我看来是这样)。
This document is an attempt to provide a clear and simple no-nonsense explanation of CRCs and to absolutely nail down every detail of the operation of their high-speed implementations. In addition to this, this document presents a parameterized model CRC algorithm called the "Rocksoft^tm Model CRC Algorithm". The model algorithm can be parameterized to behave like most of the CRC implementations around, and so acts as a good reference for describing particular algorithms.
本文档试图对CRC进行清晰简单的解释,并绝对确定其高速实现操作的每一个细节。除此之外,本文还介绍了一种名为“Rocksoft^tm模型CRC算法”的参数化模型CRC算法。模型算法可以参数化,以表现得像周围的大多数CRC实现一样,因此可以作为描述特定算法的良好参考。
A low-speed implementation of the model CRC algorithm is provided in the C programming language. Lastly there is a section giving two forms of high-speed table driven implementations, and providing a program that generates CRC lookup tables.
用C编程语言提供了模型CRC算法的低速实现。最后,有一节给出了两种形式的高速表驱动实现,并提供了一个生成CRC查找表的程序。
1. 介绍: 错误检测
The aim of an error detection technique is to enable the receiver of a message transmitted through a noisy (error-introducing) channel to determine whether the message has been corrupted. To do this, the transmitter constructs a value (called a checksum) that is a function of the message, and appends it to the message. The receiver can then use the same function to calculate the checksum of the received message and compare it with the appended checksum to see if the message was correctly received. For example, if we chose a checksum function which was simply the sum of the bytes in the message mod 256 (i.e. modulo 256), then it might go something as follows. All numbers are in decimal.
错误检测的目的是让信息的接收者能够判别从有噪声的信道传来的信息是否被干扰。为了达到这样的目的,发送者使用某个以消息为参数的函数式构造出一个值(校验和),并将该值附加在信息的后面。接收者使用相同的函数计算出接收到的信息的校验和,并和附加其尾部的校验和比较,从而判断出接收到的信息是否正确。比如说,在mod 256中,我们选择的校验和函数仅是各个字节的和,则请看下面,所有的数字都是十进制的。
信息 : 6 23 4
信息加校验和 : 6 23 4 33
传输后的信息 : 6 27 4 33
In the above, the second byte of the message was corrupted from 23 to 27 by the communications channel. However, the receiver can detect this by comparing the transmitted checksum (33) with the computer checksum of 37 (6 + 27 + 4). If the checksum itself is corrupted, a correctly transmitted message might be incorrectly identified as a corrupted one. However, this is a safe-side failure. A dangerous-side failure occurs where the message and/or checksum is corrupted in a manner that results in a transmission that is internally consistent. Unfortunately, this possibility is completely unavoidable and the best that can be done is to minimize its probability by increasing the amount of information in the checksum (e.g. widening the checksum from one byte to two bytes).
如上,消息的第二字节"23"由于信道干扰变成了27,然而接收者能够通过比较传输过来的校验和值(33)同计算出来的校验和值 37(6 + 27 + 4)检测这种变化。同理,如果校验和自身被干扰了,则正确的信息可能被验证为错误的,然而这是一种良性的误判。恶性的 误判则发生在信息和/或校验和被这样的方式干扰——传输结果的内部一致。不幸的是这样的误判不可避免,我们能做的是通过增加校验和中信息的数量(如加宽校验和从1byte到2byte )来使这样的可能性最小化。
Other error detection techniques exist that involve performing complex transformations on the message to inject it with redundant information. However, this document addresses only CRC algorithms, which fall into the class of error detection algorithms that leave the data intact and append a checksum on the end. i.e.:
其它错误检测技术通过对信息的复杂变换,将冗余信息加入其中。但是, 本文只讲CRC 算法——一种可归类为使数据保持原状并在其后附加校验和的算法。例如:
<原始信息><校验和>
2. 在复杂之前的准备
In the checksum example in the previous section, we saw how a corrupted message was detected using a checksum algorithm that simply sums the bytes in the message mod 256:
在前面的校验和例子里,我们看见了被干扰了的信息是如何通过校验和(简单的将字节以mod256相加)被检测出来。
A problem with this algorithm is that it is too simple. If a number of random corruptions occur, there is a 1 in 256 chance that they will not be detected. For example:
但这个算法过于简单了,一个随机的干扰有1/256的概率使其不会被检测出来(因为是累加和,无论之前的数据是多少在最后一个字节肯定能从256个数字中找到1个和校验和相同,因此有1/256的概率出错),比如:
信息: 6 23 4
信息加校验和: 6 23 4 33
传输后的信息: 8 20 5 33
To strengthen the checksum, we could change from an 8-bit register to a 16-bit register (i.e. sum the bytes mod 65536 instead of mod 256) so as to apparently reduce the probability of failure from 1/256 to 1/65536. While basically a good idea, it fails in this case because the formula used is not sufficiently "random"; with a simple summing formula, each incoming byte affects roughly only one byte of the summing register no matter how wide it is. For example, in the second example above, the summing register could be a Megabyte wide, and the error would still go undetected. This problem can only be solved by replacing the simple summing formula with a more sophisticated formula that causes each incoming byte to have an effect on the entire checksum register.
为了加强校验和,我们可以通过将8为寄存器改为16位寄存器(比如将字节以mod65536相加替代以mod256相加)来降低检测失败的概率为1/65536。这仅是一个简单的好想法,他的不足在于不够“随机”;在这个的简单算法里,不论这个校验和寄存器有多长,接收到的每一个字节只会影响校验和寄存器的一个字节。比如在上面的第二个例子里,即使校验和寄存器达到了兆字节宽,错误仍然检测不出来。这个问题只能通过引入一种使接收到字节能够对整个校验和寄存器造成的影响的更复杂的算法来解决。
Thus, we see that at least two aspects are required to form a strong checksum function:
因此,我们意识到至少有两方面影响了校验和函数的强健性:
-
宽度(WIDTH):
A register width wide enough to provide a low a-priori probability of failure (e.g. 32-bits gives a 1/2^32 chance of failure).
寄存器宽度足够预先提供一个低的失败可能性(e.g. 32-bits gives a 1/2^32 chance of failure)
-
扰乱度(CHAOS):
A formula that gives each input byte the potential to change any number of bits in the register.
通过某个公式使每一位接收到的字节都有改变寄存器内任何bit位的可能。
Note: The term "checksum" was presumably used to describe early summing formulas, but has now taken on a more general meaning encompassing more sophisticated algorithms such as the CRC ones. The CRC algorithms to be described satisfy the second condition very well, and can be configured to operate with a variety of checksum widths.
Note: 术语"校验和"被认为用来描述早期的相加的校验方式,但是现在有了更广泛的意义,包括了更先进的算法,比如CRC算法。CRC算法被认为能很好满足第二个条件,并能在不同的校验和宽度上得到运用。
3. CRC算法的基本思想
Where might we go in our search for a more complex function than summing? All sorts of schemes spring to mind. We could construct tables using the digits of pi, or hash each incoming byte with all the bytes in the register. We could even keep a large telephone book on-line, and use each incoming byte combined with the register bytes to index a new phone number which would be the next register value. The possibilities are limitless.
我们应从何处开始来寻找比相加更复杂的函数呢?各种各样的想法涌入脑中。我们可以用pi值构建一张表,或用寄存器的每一位哈希(hash )接收到的字节。我们甚至可以保有一本巨大的“电话簿”,用接收到的字节结合寄存器中的字节来索引一个号码作为下一个寄存器中的值。可行的方案很多。
However, we do not need to go so far; the next arithmetic step suffices. While addition is clearly not strong enough to form an effective checksum, it turns out that division is, so long as the divisor is about as wide as the checksum register.
但现在 ,我们不必想太远,接下来的算法步骤就足够了。加法明显的不够强大来形成一种有效的校验和机制,但除法却可以。 只要除数和校验和寄存器大约一样宽就行。
The basic idea of CRC algorithms is simply to treat the message as an enormous binary number, to divide it by another fixed binary number, and to make the remainder from this division the checksum. Upon receipt of the message, the receiver can perform the same division and compare the remainder with the "checksum" (transmitted remainder).
CRC算法的基本思想简单地就是将信息看成是一个大的二进制数,然后用另一个固定的二进制数来除它,得到余数。接收方用同样的除法来除接收到的信息,将得到的余数同传输来的校验和(一个余数)进行比较。
Example: Suppose the the message consisted of the two bytes (6,23) as in the previous example. These can be considered to be the hexadecimal number 0617 which can be considered to be the binary number 0000-0110-0001-0111. Suppose that we use a checksum register one-byte wide and use a constant divisor of 1001, then the checksum is the remainder after 0000-0110-0001-0111 is divided by 1001. While in this case, this calculation could obviously be performed using common garden variety 32-bit registers, in the general case this is messy. So instead, we'll do the division using good-'ol long division which you learned in school (remember?). Except this time, it's in binary:
例子:如同前面的例子一样,假定信息由两个字节(6,23 )组成,16进制是0617 ,二进制是0000-0110-0001-0111。同时还假定使用用一字节宽的校验和寄存器 ,除数恒为1001。 那么校验和就是0000-0110-0001-0111除以1001的余数。者显然可以用一般的32位寄存器来完成计算,但大多数情况下这样比较麻烦。所以,我们用长除法 ,这在学校里学过(还记得吗?)。只是本次是二进制了。
...0000010101101 = 00AD = 173 = QUOTIENT
____-___-___-___-
9= 1001 ) 0000011000010111 = 0617 = 1559 = DIVIDEND
DIVISOR 0000.,,....,.,,,
----.,,....,.,,,
0000,,....,.,,,
0000,,....,.,,,
----,,....,.,,,
0001,....,.,,,
0000,....,.,,,
----,....,.,,,
0011....,.,,,
0000....,.,,,
----....,.,,,
0110...,.,,,
0000...,.,,,
----...,.,,,
1100..,.,,,
1001..,.,,,
====..,.,,,
0110.,.,,,
0000.,.,,,
----.,.,,,
1100,.,,,
1001,.,,,
====,.,,,
0111.,,,
0000.,,,
----.,,,
1110,,,
1001,,,
====,,,
1011,,
1001,,
====,,
0101,
0000,
----
1011
1001
====
0010 = 02 = 2 = REMAINDER
In decimal this is "0x0617=1559 divided by 9 is 173 with a remainder of 2".
在十进制里为1559/9=173 余2。
Although the effect of each bit of the input message on the quotient is not all that significant, the 4-bit remainder gets kicked about quite a lot during the calculation, and if more bytes were added to the message (dividend) it's value could change radically again very quickly. This is why division works where addition doesn't.
虽然得到信息的每一bit对商的影响并不都一样明显,但4bit的余数却在计算时不停变换。并且如果更多的字节加入信息(被除数),余数的值会再次迅速的发生巨大变化。这就是加法办不到的地方。
In case you're wondering, using this 4-bit checksum the transmitted message would look like this (in hexadecimal): 06172 (where the 0617 is the message and the 2 is the checksum). The receiver would divide 0617 by 9 and see whether the remainder was 2.
此时你能想象的到,用4bit校验的信息将会这样被传输(16进制):06172(0617是信息,2是校验和),接收器者用9来除0617,看余数是否为2。
4. 多项式算术
While the division scheme described in the previous section is very very similar to the checksumming schemes called CRC schemes, the CRC schemes are in fact a bit weirder, and we need to delve into some strange number systems to understand them.
前面讲的除法方式已经很像CRC算法了,CRC算法实际上比他稍微麻烦一点,我们需要探讨一些奇特的数字系统来理解它。
The word you will hear all the time when dealing with CRC algorithms is the word "polynomial". A given CRC algorithm will be said to be using a particular polynomial, and CRC algorithms in general are said to be operating using polynomial arithmetic. What does this mean?
CRC 算法里时你将经常听见多项式一词。一个特定的 CRC 算法被说成是用一个特别的多项式,CRC 算法被普遍认为是一种多项是算法,这是什么意思呢?
Instead of the divisor, dividend (message), quotient, and remainder (as described in the previous section) being viewed as positive integers, they are viewed as polynomials with binary coefficients. This is done by treating each number as a bit-string whose bits are the coefficients of a polynomial. For example, the ordinary number 23 (decimal) is 17 (hex) and 10111 binary and so it corresponds to the polynomial:
除数,被除数,商,余数在这里被看成是有二进制系数的多项式而不是正整数。这是因为将每一个数看作是比特串(bit-string ),比特位就是多项式系数。比如,23(dec)是17(hex)和10111(bin),所以多项式形式为:
或更简单的写为:
Using this technique, the message, and the divisor can be represented as polynomials and we can do all our arithmetic just as before, except that now it's all cluttered up with Xs. For example, suppose we wanted to multiply 1101 by 1011. We can do this simply by multiplying the polynomials:
用这种方法,信息和除数可看成是多项式,并且我们可以用前面使用的算术方法,只是被写成了 Xs的形式。比如,我们想将1101 和 1011相乘。则我们将如下多项式相乘:
At this point, to get the right answer, we have to pretend that x is 2 and propagate binary carries from the 3*x^3 yielding:
这时,为了得到正确的答案,我们不得不假设x为2,将3*x^3进位得:
It's just like ordinary arithmetic except that the base is abstracted and brought into all the calculations explicitly instead of being there implicitly. So what's the point?
这就像是普通的算术,只不过基数被抽象化了,由此造成除法计算显得不够显式, 这样有什么意义呢?
The point is that IF we pretend that we DON'T know what x is, we CAN'T perform the carries. We don't know that 3*x^3 is the same as x^4 + x^3 because we don't know that x is 2. In this true polynomial arithmetic the relationship between all the coefficients is unknown and so the coefficients of each power effectively becomestrongly typed; coefficients of x^2 are effectively of a different type to coefficients of x^3.
意义在于如果我们假定不知道x是多少,我们就不能做进位运算,我们就不知道 \(3*x^3\) 与 \(x^4 + x^3\) 等价,因为我们不知道x是2。在这个真多项式算术中,系数间的关系是未知的,因此每个幂(power )的系数变成了强类型;这样x^2 的系数的对于x^3就是一种不同的类型。
With the coefficients of each power nicely isolated, mathematicians came up with all sorts of different kinds of polynomial arithmetics simply by changing the rules about how coefficients work. Of these schemes, one in particular is relevant here, and that is a polynomial arithmetic where the coefficients are calculated MOD 2 and there is no carry; all coefficients must be either 0 or 1 and no carries are calculated. This is called "polynomial arithmetic mod 2". Thus, returning to the earlier example:
针对不同的幂的系数完全无关这一点,数学家提出了各种各样的多项式算术方法,这些算术方法简单的通过改变系数的运算的法法则来解决上述问题。在这些方法中有一种和crc相关,是一种将系数通过模2和计算,没有进位;所有系数必须是0或1,无进位。被称作“多项式模2和算法”("polynomial arithmetic mod 2")。因此,回到早先的例子中:
Under the other arithmetic, the 3*x^3 term was propagated using the carry mechanism using the knowledge that x=2. Under "polynomial arithmetic mod 2", we don't know what x is, there are no carries, and all coefficients have to be calculated mod 2. Thus, the result becomes:
在其他算术方式下, 3*x^3在知道x="2"的情况下来进位,在“多项式模2和算法”下,我们不知道x是几,这里没有进位,所有系数通过模2和计算。因此,结果变为:
Thus polynomical arithmetic mod 2 is just binary arithmetic mod 2 with no carries. While polynomials provide useful mathematical machinery in more analytical approaches to CRC and error-correction algorithms, for the purposes of exposition they provide no extra insight and some encumbrance and have been discarded in the remainder of this document in favour of direct manipulation of the arithmetical system with which they are isomorphic: binary arithmetic with no carry.
因此多项式模2和算法仅仅是没有进位的二进制模2算法。虽然它为分析crc和纠错算法提供了有用的数学机理,但由于其没有进一步的内容并显得有些繁缛,所以在随后的文章中不再阐述,取而代之的是与其同型的算法:没有进位的二进制算术。
5. Binary Arithmetic with No Carries 无进位二进制算术
Having dispensed with polynomials, we can focus on the real arithmetic issue, which is that all the arithmetic performed during CRC calculations is performed in binary with no carries. Often this is called polynomial arithmetic, but as I have declared the rest of this document a polynomial free zone, we'll have to call it CRC arithmetic instead. As this arithmetic is a key part of CRC calculations, we'd better get used to it. Here we go:
在省略了多项式之后,我们可以专注于真正的算术问题,即CRC计算期间执行的所有算术都是在二进制中执行的,没有进位。这通常被称为多项式算术,但正如我在本文档的其余部分所声明的那样,我们必须称之为CRC算术。由于此算法是CRC计算的关键部分,我们最好习惯它
Adding two numbers in CRC arithmetic is the same as adding numbers in ordinary binary arithmetic except there is no carry. This means that each pair of corresponding bits determine the corresponding output bit without reference to any other bit positions. For example:
CRC算术中的两个数字相加与普通二进制算术中的数字相加相同,除了没有进位。这意味着每对对应的比特确定对应的输出比特,而不参考任何其他比特位置。例如
10011011
+11001010
--------
01010001
--------
There are only four cases for each bit position:
0+0=0
0+1=1
1+0=1
1+1=0 (no carry)
Subtraction is identical:
10011011
-11001010
--------
01010001
--------
with
0-0=0
0-1=1 (wraparound)
1-0=1
1-1=0
In fact, both addition and subtraction in CRC arithmetic is equivalent to the XOR operation, and the XOR operation is its own inverse. This effectively reduces the operations of the first level of power (addition, subtraction) to a single operation that is its own inverse. This is a very convenient property of the arithmetic.
事实上,CRC算法中的加法和减法都等价于XOR运算,XOR运算本身就是逆运算。这有效地将第一级功率(加法、减法)的操作减少到其自身的逆操作。这是算术的一个非常方便的性质。
By collapsing of addition and subtraction, the arithmetic discards any notion of magnitude beyond the power of its highest one bit. While it seems clear that 1010 is greater than 10, it is no longer the case that 1010 can be considered to be greater than 1001. To see this, note that you can get from 1010 to 1001 by both adding and subtracting the same quantity:
通过折叠加法和减法,该算法丢弃了任何超过其最高一位幂的幅度概念。虽然1010显然大于10,但1010不再可以被视为大于1001。要看到这一点,请注意,通过加减相同的数量,可以从1010到1001:
1001 = 1010 + 0011
1010 = 1001 - 0011
This makes nonsense of any notion of order.
这使得任何秩序的概念都变得毫无意义。
Having defined addition, we can move to multiplication and division. Multiplication is absolutely straightforward, being the sum of the first number, shifted in accordance with the second number.
定义了加法后,我们可以进行乘法和除法。乘法是绝对简单的,是第一个数字的总和,根据第二个数字进行移位。
1101
x 1011
----
1101
1101.
0000..
1101...
-------
1111111 Note: The sum uses CRC addition
-------
Division is a little messier as we need to know when "a number goes into another number". To do this, we invoke the weak definition of magnitude defined earlier: that X is greater than or equal to Y iff the position of the highest 1 bit of X is the same or greater than the position of the highest 1 bit of Y. Here's a fully worked division (nicked from [Tanenbaum81]).
除法有点混乱,因为我们需要知道什么时候“一个数字变成另一个数字”。为此,我们调用前面定义的弱震级定义:如果X的最高1位的位置等于或大于Y的最高1位数的位置,则X大于或等于Y。这是一个完全有效的除法(从[Tanenbaum81]中提取)。
1100001010
_______________
10011 ) 11010110110000
10011,,.,,|.|.
-----,,.,,|...
10011,.,,|.|.
10011,.,,|...
-----,.,,|.|.
10110...
10011.|.
-----...
010100.
10011.
-----.
01110
00000
-----
1110 = Remainder
That's really it. Before proceeding further, however, it's worth our while playing with this arithmetic a bit to get used to it.
确实如此。然而,在继续之前,值得我们花点时间玩一下这个算法来习惯它。
We've already played with addition and subtraction, noticing that they are the same thing. Here, though, we should note that in this arithmetic A+0=A and A-0=A. This obvious property is very useful later.
我们已经玩过加法和减法,注意到它们是一样的。不过,在这里,我们应该注意到,在这个算术中,A+0=A和A-0=A。这个明显的属性在以后非常有用。
In dealing with CRC multiplication and division, it's worth getting a feel for the concepts of MULTIPLE and DIVISIBLE.
在处理CRC乘法和除法时,有必要了解一下MULTIPLE和DIVISIBLE的概念。
If a number A is a multiple of B then what this means in CRC arithmetic is that it is possible to construct A from zero by XORing in various shifts of B. For example, if A was 0111010110 and B was 11, we could construct A from B as follows:
如果一个数字a是B的倍数,那么在CRC算法中,这意味着可以通过在B的不同移位中进行异或从零构造a。例如,如果a是0111010110,B是11,我们可以按如下方式从B构造a:
0111010110
= .......11.
+ ....11....
+ ...11.....
+ .11.......
0111010111
= ........11
+ ......11..
+ .....11...
+ ..11......
01........ (被除数和除数的奇偶数不一致)
However, if A is 0111010111, it is not possible to construct it out of various shifts of B (can you see why? - see later) so it is said to be not divisible by B in CRC arithmetic.
然而,如果A是0111010111,则不可能用B的各种移位来构造它(你能明白为什么吗?-见下文),因此在CRC算法中,它被认为不能被B整除。
Thus we see that CRC arithmetic is primarily about XORing particular values at various shifting offsets.
因此,我们看到CRC算法主要是在各种偏移量下对特定值进行异或运算。