这几乎肯定是一个非常愚蠢的问题,但由于某种原因,我在网络校验和计算方面遇到了麻烦。所有算法基本上都是这样的:
WORD chksm(WORD *startpos, WORD checklen){
ulong sum = 0;
WORD answer = 0;
while (checklen > 1)
{
sum += *startpos++;
checklen -= 2;
}
if (checklen == 1)
{
*(BYTE *)(&answer) = *(BYTE *)startpos;
sum += answer;
}
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
answer = ~sum;
return answer;}
我清楚除了这条线以外的一切:
sum += (sum >> 16);
它看起来像是在它将前16位加到后16位之前的行,将所有零都保留在前16位中。如果是这种情况,那么就不会总结>> 16现在等于零?如果是这样,为什么那条线呢?
或者我(可能)今天只是完全精神衰竭?
答案 0 :(得分:4)
这是一个补码和的定义的一部分。您获取任何溢出位并将它们添加回低16位。将它们添加回来可能会导致进一步溢出,所以重复此操作直到高位结束为零。所以,概念上就是这样:
while (sum >> 16 != 0) {
sum = (sum >> 16) + (sum & 0xffff);
}
然而,这个循环最多只能执行两次,所以不需要显式循环。在第一次加法之后,可能有或没有溢出,进位位在高16位中结束。在这种情况下,高16位将为0x0001
,您将不得不再做一次添加以将该进位添加回来。
想象一下最坏的情况,在初始while循环之后sum总和为0xffffffff
。然后添加将如下进行:
sum = (0xffffffff >> 16) + (0xffffffff & 0xffff)
= 0xffff + 0xffff
= 0x1fffe
sum = (0x1fffe >> 16) + (0x1fffe & 0xffff)
= 0x1 + 0xfffe
= 0xffff
我们完成了两个新增功能,因为高16位现在已经清除了。这是最糟糕的情况,因此循环可以展开为两个添加。
(而然后毕竟你拿了最后一笔的一个补码,导致这个名字非常混乱:一个补码的补码。我花了很长时间在我第一次实现它的时候绕过这个 - 特别是一个补码和不涉及~
补码运算符。)
答案 1 :(得分:3)
你几乎是对的。
由于进位,高16位可能为1。
例如,FFFF + FFFF => 1FFFE
或FFFF + 1 => 10000
。
答案 2 :(得分:1)
我认为ulong是32位宽,这意味着:
sum = (sum >> 16) + (sum & 0xffff)
sum += (sum >> 16);
将顶部的sicteen位和底部sisteen位加在一起。然后下一行将前十六位的结果相加;由于进位操作可能会有一个。