开发者社区> 问答> 正文

二进制反码求和的二进制反码求和的计算方法:

二进制反码求和的二进制反码求和的计算方法:

展开
收起
知与谁同 2018-07-19 13:56:24 4054 0
1 条回答
写回答
取消 提交回答
  • 首先,我们计算如图B-1所示的部分和。我们把每一列相加,如果有进位,就加到下一列。注意以下几点:
    1-->16
    1 1-->15
    * 1-->14
    * 1-->13
    * * 1 1-->12
    * * * 1-->11
    * * * * 1-->10
    * * * * * 1 1-->9
    * * * * * * * 1 1-->7
    * * * * * * * * * 1-->6
    * * * * * * * * * 1-->5
    * * * * * * * * * 1-->4
    * * * * * * * * * * 1-->3
    * * * * * * * * * * * * 1 1-->2
    * * * * * * * * * * * * 1-->第1的进位,以上同义(右起为第一列)
    1 0 0 1 1 0 0 1 0 0 0 1 0 0 1 0
    0 0 0 0 1 0 0 0 0 1 1 0 1 0 0 1
    1 0 1 0 1 0 1 1 0 0 0 0 0 0 1 0
    0 0 0 0 1 1 1 0 0 0 0 0 1 0 1 0
    0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1
    0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1
    0 0 0 0 0 1 0 0 0 0 1 1 1 1 1 1
    0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 1
    0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1
    0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
    0 1 0 1 0 1 0 0 0 1 0 0 0 1 0 1
    0 1 0 1 0 0 1 1 0 1 0 1 0 1 0 0
    0 1 0 0 1 0 0 1 0 1 0 0 1 1 1 1
    0 1 0 0 0 1 1 1 0 0 0 0 0 0 0 0
    1 0 0 1 0 1 1 0 1 1 1 0 1 0 1 0 部分和
    1 -->第15列的进位
    1 -->第16列的进位
    1 0 0 1 0 1 1 0 1 1 1 0 1 1 0 0 和
    0 1 1 0 1 0 0 1 0 0 0 1 0 0 1 1 校验和
    图B-1 二进制记法的部分和
    1,当我们加第1列(最右边一列)的时候,我们得到8。在二进制中,数8是1000。我们保留最右边的0,把其余的位进到第2列,第3列和第4列。
    2,当我们加第2列时,我们计入从第1列来的进位。结果是7,它是二进制的0111。我们保留第一个位(最右边的),把其余011进位给第3列、第4列和第5列。
    3,对每一列重复以上过程。
    4,当我们加完最后一列时,我们有两个1没有列可以进行进位。这两个1在下一个步骤中应与部分和(Partial sum)相加。
    B.1.2和
    如果最后一列没有进位,那么部分和就是和。但是,如果还有额外的列(在本例中,有一个具有两行的列),那么就要把它加到部分和中,以便得出和。下图给出了这样的计算,我们得出了和。
    1 0 0 1 0 1 1 0 1 1 1 0 1 0 1 0 部分和
    1 0 -->第15,16列的进位
    1 0 0 1 0 1 1 0 1 1 1 0 1 1 0 0 和
    0 1 1 0 1 0 0 1 0 0 0 1 0 0 1 1 校验和
    图B-2 二进制记法的和与校验和
    B.1.2校验和
    在计算出和以后,我们把每一个位求反码,得出检验和。图B-2也给出了检验和。二进制计算方法其实可以转换为十进制计算,原理相同。
    算法的实现:
    首先,查看了Linux 2.6内核中的校验算法,使用汇编语言编写的,显然效率要高些。代码如下:
    unsigned short ip_fast_csum(unsigned char * iph,
    unsigned int ihl)
    {
    unsigned int sum;
    __asm__ __volatile__(
    "movl (%1), %0 ;\n"
    "subl $4, %2 ;\n"
    "jbe 2f ;\n"
    "addl 4(%1), %0 ;\n"
    "adcl 8(%1), %0 ;\n"
    "adcl 12(%1), %0 ;\n"
    "1: adcl 16(%1), %0 ;\n"
    "lea 4(%1), %1 ;\n"
    "decl %2 ;\n"
    "jne 1b ;\n"
    "adcl $0, %0 ;\n"
    "movl %0, %2 ;\n"
    "shrl $16, %0 ;\n"
    "addw %w2, %w0 ;\n"
    "adcl $0, %0 ;\n"
    "notl %0 ;\n"
    "2: ;\n"
    /* Since the input registers which are loaded with iph and ihl
    are modified, we must also specify them as outputs, or gcc
    will assume they contain their original values. */
    : "=r" (sum), "=r" (iph), "=r" (ihl)
    : "1" (iph), "2" (ihl)
    : "memory");
    return(sum);
    }
    在这个函数中,第一个参数显然就是IP数据报的首地址,所有算法几乎一样。需要注意的是第二个参数,它是直接使用IP数据报头信息中的首部长度字段,不需要进行转换,因此,速度又快了(高手就是考虑的周到)。使用方法会在下面的例子代码中给出。
    第二种算法就非常普通了,是用C语言编写的。我看了许多实现网络协议栈的代码,这个算法是最常用的了,即使变化,也无非是先取反后取和之类的。考虑其原因,估计还是C语言的移植性更好吧。下面是该函数的实现:
    unsigned short checksum(unsigned short *buf,int nword)
    {
    unsigned long sum;
    for(sum=0;nword>0;nword--)
    {
    sum += *buf++;
    sum = (sum>>16) + (sum&0xffff);
    }
    return ~sum;
    }

    2019-07-17 22:58:03
    赞同 展开评论 打赏
问答地址:
问答排行榜
最热
最新

相关电子书

更多
低代码开发师(初级)实战教程 立即下载
冬季实战营第三期:MySQL数据库进阶实战 立即下载
阿里巴巴DevOps 最佳实践手册 立即下载