最近准备开始更新一个算法系列,这个系列嘛就是别管有没有用先学为敬系列(或者现学现卖系列)。那么这个系列的第一篇就用我这两天看的CRC算法当作开篇吧。
1)什么是CRC呢?
如百度所诉,好像是一个十分有用的算法,下为百度内容
CRC(Cyclic Redundancy Check),即循环冗余校核,是一种根据网络数据包或电脑文件等数据产生简短固定位数校核码的快速算法,主要用来检测或校核数据传输或者保存后可能出现的错误。CRC利用除法及余数的原理,实现错误侦测的功能,具有原理清晰、实现简单等优点。
2)CRC原理是什么?
先需要了解一个概念,叫做二进制除法,这里我看了很多资料。我的理解并不是除法,而是一种将一串较长的二进制数据从高位开始对一个较短的二进制数据位异或,得到的结果如果高位为0则左移这个较长数据继续位异或,直到移动到最低位以后,则最终的异或结果为余数。我想之所以被叫做除法是因为和除法的规律一致,有被除数,商和余数。
下面贴两张我写算法过程中计算的两张图,也会和后面的源代码对应
crc 就是利用这个二进制除法的公式去进行校验的。首先找一个公共的被除数,然后将原始数据左移这个被除数的长度在末尾补0当除数,然后用二进制除法计算出余数,再将余数加到除数上。得出的结果作为数据传输。这样在做数据校验的时候,只需要将这个结果除以之前的被除数,如果没有余数就说明数据传输过程没有丢失。这个原理我自己思考了一下,为什么要在末尾补0的问题。我想主要是不影响原始数据吧。因为这样得到的余数正好可以加到原始数据的后八位。而在后续进行校验的时候不需要再对数据做任何处理,因为用来计算的数据就是 ,高8位数原始数据,低八位是计算后得到的余数,校验的数据直接使用传输数据即可。(这里不一定是低8位,位之所以用8位当作被除数的长度,是因为8位正好一个字节计算起来更简单,不用做一些位移操作)。或者反过来想,如果不做位移那么得到的余数会受到原始数据长度的影响,会让算法写起来更加复杂。
下面就是我用c++写的算法,由于我还是一个c++的初学者,所以写的可能不太好。但输出结果是对的,是符合crc算法原理的。打印二进制的方法是参照了别人的代码进行修改的,只是为了方便查看结果是否正确。整段代码是可以直接运行的,运行结果放在下面。
以上就是我努力了一天才写好的crc。最开始真的是卡在了原理,然后今天早上看到一个博主写的非常明白的原理,才有了这篇博客。推荐一下这篇博客
CRC校验原理及步骤_erci_fc2336的博客-CSDN博客_crc校验
需要注意c++右移会带着符号位,因此右移需要使用无符号类型
#include "stdio.h"
#include "stdlib.h"
#include "iostream"
using namespace std;
// 代码格式化 option+shift+f
int16_t testCrc(int8_t data);
int16_t checkCrc(int16_t data);
int main()
{
int8_t data = 0b10001010;
int16_t result = testCrc(data);
checkCrc(result);
return 0;
}
void print16binry(int16_t num)
{
int count = (sizeof(num) << 3) - 1;
while (count >= 0)
{
int16_t bitnum = num >> count;
int16_t byte = bitnum & 1;
printf("%d", byte);
// if (count%4==0) {
if (count % 8 == 0)
{
printf(" ");
}
count--;
}
printf("\n");
}
void print8binry(int8_t num)
{
int count = (sizeof(num) << 3) - 1;
while (count >= 0)
{
int8_t bitnum = num >> count;
int8_t byte = bitnum & 1;
printf("%d", byte);
// if (count%4==0) {
if (count % 8 == 0)
{
printf(" ");
}
count--;
}
printf("\n");
}
void printUnsighedBinry(unsigned int num)
{
int count = (sizeof(num) << 3) - 1;
while (count >= 0)
{
unsigned int bitnum = num >> count;
unsigned int byte = bitnum & 1;
printf("%d", byte);
// if (count%4==0) {
if (count % 8 == 0)
{
printf(" ");
}
count--;
}
printf("\n");
}
int16_t testCrc(int8_t data)
{
int16_t result = 0; // 结果
int8_t dividend = 0b10010101; // 被除数
int16_t wdata = data << 8; // 讲数据左移到高8位
printf("wdata:");
print16binry(wdata);
unsigned int divisor = data << 8;
// int16_t divisor = 0b0 | data;
// printf("divisor:");
// printbinry(divisor);
int bit = 8; // 位移的位数
int8_t temp = 0;
temp = divisor >> bit;
int8_t moveCount = 0;
for (int i = bit; i > 0; i--)
{
printf("temp:");
print8binry(temp);
temp = temp ^ dividend;
printf("temp2:");
print8binry(temp);
int8_t high0 = 0; // 异或结果的高位0
for (int i = 7; i > 0; i--)
{
if ((temp >> i) > 0)
{
break;
}
else
{
moveCount++;
high0++;
if (moveCount == 8)
{
break;
}
}
};
temp = temp << high0;
if (moveCount == 8)
{
break;
}
};
unsigned int unsingedTemp=temp;
unsingedTemp= unsingedTemp<<24;
unsingedTemp= unsingedTemp>>24;
result = wdata | unsingedTemp;
printf("result:");
printUnsighedBinry(unsingedTemp);
print16binry(result);
printf("\n");
return result;
}
int16_t checkCrc(int16_t data)
{
int16_t result = 0;
int8_t dividend = 0b10010101;
int8_t completeTemp = data;
unsigned int completeW = completeTemp;//使用无符号int
printf("init completeW:");
printUnsighedBinry(completeW);
printf("data:");
print16binry(data);
int16_t divisor = data;
// int16_t divisor = 0b0 | data;
// printf("divisor:");
// printbinry(divisor);
// int16_t divisor = 0b0 | data;
// printf("divisor:");
// printbinry(divisor);
int bit = 8; // 位移的位数
int8_t temp = 0;
temp = divisor >> bit; // 从高8位开始
int8_t moveCount = 0;
int8_t high0 = 0;
for (int i = bit; i > 0; i--)
{
printf("temp:");
print8binry(temp);
temp = temp ^ dividend;
printf("temp2:");
print8binry(temp);
completeW = completeW<<24;
completeW = completeW<<high0;
completeW = completeW>>24;//这里之所以移来移去是需要使用低八位。无符号的int似乎只有 32位的
printf("completeW:");
printUnsighedBinry(completeW);
printUnsighedBinry(completeW);
high0 = 0; // 异或结果的高位0
for (int i = 7; i > 0; i--)
{
if ((temp >> i) > 0)
{
break;
}
else
{
moveCount++;
high0++;
if (moveCount == 8)
{
break;
}
}
};
// print16binry(completeW);
int16_t tempw = temp << 8; // 左移以后使用数据的后8位补充
printf("tempw1:");
print16binry(tempw);
tempw = tempw | completeW;
printf("tempw:");
print16binry(tempw);
tempw = tempw << high0;
temp = tempw >> 8; // 保留8位进行计算
if (moveCount == 8)
{
break;
}
};
printf("result:");
print8binry(temp);
printf("\n");
return result;
}