【发布时间】:2016-10-26 19:15:14
【问题描述】:
我很难确定Fletcher checksum algorithm 的 32 位变体的哪个实现是正确的。维基百科提供了以下优化实现:
uint32_t fletcher32( uint16_t const *data, size_t words ) {
uint32_t sum1 = 0xffff, sum2 = 0xffff;
size_t tlen;
while (words) {
tlen = words >= 359 ? 359 : words;
words -= tlen;
do {
sum2 += sum1 += *data++;
} while (--tlen);
sum1 = (sum1 & 0xffff) + (sum1 >> 16);
sum2 = (sum2 & 0xffff) + (sum2 >> 16);
}
/* Second reduction step to reduce sums to 16 bits */
sum1 = (sum1 & 0xffff) + (sum1 >> 16);
sum2 = (sum2 & 0xffff) + (sum2 >> 16);
return sum2 << 16 | sum1;
}
此外,我已经改编了 Wikipedia 文章中未优化的 16 位示例来计算 32 位校验和:
uint32_t naive_fletcher32(uint16_t *data, int words) {
uint32_t sum1 = 0;
uint32_t sum2 = 0;
int index;
for( index = 0; index < words; ++index ) {
sum1 = (sum1 + data[index]) % 0xffff;
sum2 = (sum2 + sum1) % 0xffff;
}
return (sum2 << 16) | sum1;
}
这两种实现都产生相同的结果,例如0x56502d2a 用于字符串 abcdef。为了验证这确实是正确的,我试图找到该算法的其他实现:
- An online checksum/hash generator
- C++ implementation in the srecord project
- There's also a JavaScript implementation
所有这些似乎都同意 abcdef 的校验和是 0x8180255 而不是 Wikipedia 上的实现给出的值。我已将其范围缩小到实现操作的数据缓冲区。上述所有非维基百科实现一次操作一个字节,而维基百科实现使用 16 位字计算校验和。如果我修改上述“幼稚”的 Wikipedia 实现以改为按字节操作,它的内容如下:
uint32_t naive_fletcher32_per_byte(uint8_t *data, int words) {
uint32_t sum1 = 0;
uint32_t sum2 = 0;
int index;
for( index = 0; index < words; ++index ) {
sum1 = (sum1 + data[index]) % 0xffff;
sum2 = (sum2 + sum1) % 0xffff;
}
return (sum2 << 16) | sum1;
}
唯一改变的是签名,真的。所以这个修改后的朴素实现和上面提到的实现(除了维基百科)同意abcdef的校验和确实是0x8180255。
我现在的问题是:哪个是正确的?
【问题讨论】:
-
在
naive_fletcher中,循环中的% 0xffff是不必要的。您可以在循环之后执行此操作。 -
这就是为什么我认为它是幼稚的实现 :D 感谢您的提示,但问题并不是关于优化 :)
-
@PaulOgilvie:
% 0xffff in the loop are not necessary只要没有溢出。 -
@greybeard,溢出会发生什么?永远不会使用的位只会从寄存器中掉出来。
-
@PaulOgilvie: 0x10000%0xffff 是 1,而不是 0:携带需要计入。
标签: c algorithm checksum correctness