From:http://blog.csdn.net/sparkliang/article/details/5671543

CRC32算法详细推导(3)

郁闷的位逆转

看起来我们已经得到 CRC-32 算法的最终形式了,可是、可是在实际的应用中,数据传输时是低位先行的;对于一个字节 Byte 来讲,传输将是按照 b1,b2,...,b8 的顺序。而我们上面的算法是按照高位在前的约定,不管是 reg还是 G(x) , g32,g31,...,g1 ; b8,b7,...,b1 ; r32,r31,...,r1 。

先来看看前面从 bit 转换到 Byte 一节中 for 循环的逻辑:

[cpp] view plaincopy
  1. sum_poly = reg&0xFF000000;
  2. for(int j = 0; j < 8; j++)
  3. {
  4. int hi = sum_poly&0x80000000; // 测试reg最高位
  5. sum_poly <<= 1;
  6. if(hi) sum_poly = sum_poly^POLY;
  7. }
  8. // 计算步骤2
  9. reg = (reg<<8)|p[i];
  10. reg = reg ^ sum_poly;

在这里的计算中, p[i] 是按照 p8,p7,...,p1 的顺序;如果 p[i] 在这里变成了 p1,p2,...,p8 的顺序;那么 reg 也应该是 r1,r2,...,r32 的顺序,同样 G(x) 和 sum_poly 也要逆转顺序。转换后的 G(x) = POLY = 0xEDB88320 。

于是取 reg 的最高位的 sum_poly 的初值就从 sum_poly = reg & 0xFF000000 变成了 sum_poly = reg & 0xFF,测试 reg 的最高位就从 sum_poly & 0x80000000 变成了 sum_poly&0x01 ;

移出最高位也就从 sum_poly<<=1 变成了 sum_poly>>=1 ;于是上面的代码就变成了如下的形式:

[cpp] view plaincopy
  1. sum_poly = reg&0xFF;
  2. for(int j = 0; j < 8; j++)
  3. {
  4. int hi = sum_poly&0x01; // 测试reg最高位
  5. sum_poly >>= 1;
  6. if(hi) sum_poly = sum_poly^POLY;
  7. }
  8. // 计算步骤2
  9. reg = (reg<<8)|p[i];
  10. reg = reg ^ sum_poly;

为了清晰起见,给出完整的代码:

[cpp] view plaincopy
  1. // 以4 byte数据为例
  2. #define POLY 0xEDB88320L // CRC32生成多项式
  3. unsigned int CRC32_2(unsigned int data)
  4. {
  5. unsigned char p[8];
  6. memset(p, 0, sizeof(p));
  7. memcpy(p, &data, 4);
  8. unsigned int reg = 0, sum_poly = 0;
  9. for(int i = 0; i < 8; i++)
  10. {
  11. // 计算步骤1
  12. sum_poly = reg&0xFF;
  13. for(int j = 0; j < 8; j++)
  14. {
  15. int hi = sum_poly&0x01; // 测试reg最高位
  16. sum_poly >>= 1;
  17. if(hi) sum_poly = sum_poly^POLY;
  18. }
  19. // 计算步骤2
  20. reg = (reg<<8)|p[i];
  21. reg = reg ^ sum_poly;
  22. }
  23. return reg;
  24. }

依旧像上面的思路,把计算 sum_poly 的代码段提取出来,生成 256 个元素的 CRC 校验表,再修改追加 0 的逻辑,最终的代码版本就完成了,为了对比;后面给出了字节序逆转前的完整代码段。

[cpp] view plaincopy
  1. // 字节逆转后的CRC32算法,字节序为b1,b2,…,b8
  2. #define POLY 0xEDB88320L // CRC32生成多项式
  3. static unsigned int crc_table[256];
  4. unsigned int get_sum_poly(unsigned char data)
  5. {
  6. unsigned int sum_poly = data;
  7. for(int j = 0; j < 8; j++)
  8. {
  9. int hi = sum_poly&0x01; // 取得reg的最高位
  10. sum_poly >>= 1;
  11. if(hi) sum_poly = sum_poly^POLY;
  12. }
  13. return sum_poly;
  14. }
  15. void create_crc_table()
  16. {
  17. for(int i = 0; i < 256; i++)
  18. {
  19. crc_table[i] = get_sum_poly(i&0xFF);
  20. }
  21. }
  22. unsigned int CRC32_4(unsigned char* data, int len)
  23. {
  24. unsigned int reg = 0; // 0xFFFFFFFF,见后面解释
  25. for(int i = 0; i < len; i++)
  26. {
  27. reg = (reg<<8) ^ crc_table[(reg&0xFF) ^ data[i]];
  28. return reg;
  29. }
  30. }
  31. // 最终生成的校验表将是:
  32. // {0x00000000,  0x77073096,  0xEE0E612C,  0x990951BA,
  33. //  0x076DC419,  0x706AF48F,  0xE963A535,  0x9E6495A3,
  34. // … …}
  35. // 字节逆转前的CRC32算法,字节序为b8,b7,…,b1
  36. #define POLY 0x04C11DB7L // CRC32生成多项式
  37. static unsigned int crc_table[256];
  38. unsigned int get_sum_poly(unsigned char data)
  39. {
  40. unsigned int sum_poly = data;
  41. sum_poly <<= 24;
  42. for(int j = 0; j < 8; j++)
  43. {
  44. int hi = sum_poly&0x80000000; // 取得reg的最高位
  45. sum_poly <<= 1;
  46. if(hi) sum_poly = sum_poly^POLY;
  47. }
  48. return sum_poly;
  49. }
  50. void create_crc_table()
  51. {
  52. for(int i = 0; i < 256; i++)
  53. {
  54. crc_table[i] = get_sum_poly(i&0xFF);
  55. }
  56. }
  57. unsigned int CRC32_4(unsigned char* data, int len)
  58. {
  59. unsigned int reg = 0;// 0xFFFFFFFF,见后面解释
  60. for(int i = 0; i < len; i++)
  61. {
  62. reg = (reg<<8) ^ crc_table[(reg>>24)&0xFF ^ data[i]];
  63. return reg;
  64. }
  65. }

长征结束了

到这里长征终于结束了, 事实上,还有最后的一小步,那就是 reg 初始值的问题,上面的算法中 reg 初始值为 0 。在一些传输协议中,发送端并不指出消息长度,而是采用结束标志,考虑下面的这几种可能的差错:

1 )在消息之前,增加 1 个或多个 0 字节;

2) 在消息 ( 包括校验码 ) 之后,增加 1 个或多个 0 字节;

显然,这几种差错都检测不出来,其原因就是如果 reg=0 ,处理 0 消息字节 ( 或位 ) , reg 的值保持不变。解决这种问题也很简单,只要使 reg 的初始值非 0 即可,一般取 0Xffffffff ,就像你在很多 CRC32 实现中发现的那样。

到这里终于可以松一口气了, CRC32 并不是像想象的那样容易的算法啊!事实上还真不容易!这就叫做“简单的前面是优雅,背后是复杂”!

CRC32算法详细推导(3)相关推荐

  1. CRC32算法详细推导(2)

    From: http://blog.csdn.net/sparkliang/article/details/5671977 CRC算法详解(2) 初见 Table-Driven 变换到上面的方法后,我 ...

  2. 【转】卡尔曼滤波算法详细推导(相当值得一看)

    转载自   卡尔曼滤波算法详细推导     这一篇对预备知识的介绍还是很好的,过程与原理讲解也很到位,应该是目前看到中文里最好的讲解了. 一.预备知识 1.协方差矩阵     是一个维列向量,是的期望 ...

  3. Kalman滤波算法详细推导及简单匀速直线运动程序仿真(matlab)

    Kalman滤波算法详细推导及简单匀速直线运动程序仿真(matlab) 起初只是知道Kalman滤波的核心公式和会用,没有仔细研究,最近老师让讲Kalman算法,所以系统的学习了该算法,并结合匀速直线 ...

  4. 扩展卡尔曼滤波(EKF)算法详细推导及仿真(Matlab)

    前言 扩展卡尔曼滤波算法是解决非线性状态估计问题最为直接的一种处理方法,尽管EKF不是最精确的"最优"滤波器,但在过去的几十年成功地应用到许多非线性系统中.所以在学习非线性滤波问题 ...

  5. SMO算法详细推导(Sequential Minimal Optimization)

    本文针对一般性的"软判断的核函数的对偶问题的SVM",形如下式: 上式问题所在:当采样点 xix_ixi​ 选取50000个点时,则基于核函数变量Θ(xi,xj)\bm{\Thet ...

  6. 【深度学习】感知器、线性神经网络案例应用、BP神经网络算法详细推导

    感知器.线性神经网络.BP神经网络及手写数字识别 1. 单层感知器 1.1 感知器的介绍 1.2 感知器的学习规则 1.3 感知器单输入输出示例 1.4 学习率 η\etaη 1.5 模型训练收敛条件 ...

  7. lasso,lars算法详细推导过程-数学

    首发于程序员的伪文艺 关注专栏写文章 从Lasso开始说起 李新春 既可提刀立码,行遍天下:又可调参炼丹,卧于隆中. ​关注他 317 人赞同了该文章 Lasso是Least Absolute Shr ...

  8. em算法 实例 正态分布_【机器学习】EM算法详细推导和讲解

    今天不太想学习,炒个冷饭,讲讲机器学习十大算法里有名的EM算法,文章里面有些个人理解,如有错漏,还请读者不吝赐教. 众所周知,极大似然估计是一种应用很广泛的参数估计方法.例如我手头有一些东北人的身高的 ...

  9. 【2019.11.27】EM算法详细推导

    EM算法 无隐变量下,极大似然函数为: L(θ)=∏iP(xi;θ)L(\theta) = \prod_iP\left(x^{i};\theta\right) L(θ)=i∏​P(xi;θ) 含隐变量 ...

最新文章

  1. 前端工程师的mysql笔记
  2. 脱壳学习之加壳的概念
  3. 字符串处理 —— 回文串相关 —— 添加/删除字符后是否为回文串
  4. C#LeetCode刷题之#674-最长连续递增序列( Longest Continuous Increasing Subsequence)
  5. [jQuery原理] jQuery事件操作相关方法
  6. lisp坐标一键生成_Grasshopper自动生成坡度标注
  7. PyTorch 1.0 中文文档:torch.distributed
  8. 2018.9.18opencv3.4.1 + vs 2017 community +win 10 x64+cmake 3.11.3终终终章!
  9. java中GC的基本概念
  10. python实现判断给定列表是否存在重复元素,且索引差小于k
  11. 彻底卸载VS2015
  12. 拷贝相同属性的两个对象的属性-BeanUtils和PropertyUtils以及区别
  13. android tracert命令详解,Tracert命令详解
  14. 我国第一部机载脉冲火控雷达研制历程
  15. 虚拟摄像头(拉rtsp流或桌面作为图像源)
  16. 计算机二级12月报名时间广东,18年广东省全国计算机等级考试报名:12月15日起...
  17. 网站前端开发必会基础知识有哪些?
  18. java数组从小到大_JAVA中给定一个数组,怎么把它里的数从小到大排列输出?
  19. 创业知识(四):打造超强执行力团队(转载)
  20. 关于视频网站盈利模式的设想

热门文章

  1. 算命数据_未来的数据科学家或算命精神向导
  2. leetcode面试题 16.26. 计算器(栈)
  3. chrome json插件_如何使用此免费的Chrome扩展程序(或Firefox插件)获取易于阅读的JSON树
  4. angular 模块构建_通过构建全栈应用程序学习Angular 6
  5. win7无损扩大c盘空间_无损网络导航的空间模型
  6. react 手指移开_代码简介:React的五个死亡手指
  7. 到2025年将保持不变的热门流行技术
  8. 将PDF和Gutenberg文档格式转换为文本:生产中的自然语言处理
  9. MSSQL → 02:数据库结构
  10. Linq常用List操作总结,ForEach、分页、交并集、去重、SelectMany等