2

我正在实施adler32 checksum的滚动版本。

这个答案有助于仔细检查我的数学。但是我正在努力在 golang 中正确实现它。

我写了以下代码:

func roll(adler, n, leave, enter uint32) uint32 {
    a := adler & 0xffff
    b := adler >> 16

    a = (a + enter - leave) % MOD
    b = (b - n*leave - 1 + a) % MOD
    return b<<16 | a
}

它在各种输入上对其进行了测试,并且运行良好,直到我决定在随机数据上运行它。这是一个不起作用的示例(我发现了其中几个)。

令我困惑的是,python 中的相同代码在这些输入上完美运行:

def roll(adler, n, leave, enter):
    a = adler & 0xffff
    b = adler >> 16

    a = (a + enter - leave) % MOD
    b = (b - n*leave - 1 + a) % MOD
    return b<<16 | a

为了更好地衡量,我包括证明这在 python 中有效。请注意,python 校验和与 go 校验和的非滚动版本匹配(并且该部分直接来自 go 核心库)。

我研究了所有其他有问题的样本的结果,发现我从来没有在校验和的最低有效位(“a”位)上犯错。此外,误差始终相同,等于0xe10000。我怀疑 go 如何处理对 uint32 整数的模运算的特殊性是造成这种情况的原因。

发生了什么以及如何修复我的代码?

4

2 回答 2

4

Python 中的整数是有符号的。您声明 golang 版本中的所有整数都是无符号的。这就是区别。

当从较小的无符号数中减去无符号数时,您会得到一个巨大的无符号数,它在除法时给出的余数与小的负差不同。换行时,您实际上是在添加 2 32。2 32 mod 65521 是 225,或者0xe1,这就是为什么你在b. 它更有可能结束b计算,但它也可能发生a,如果a在那一步碰巧非常小。

根据@samgak 的评论,您还必须担心在不同语言中为有符号值定义的 % 运算符。因此,适用于不同约定的解决方案是MOD在执行% MOD. 对于a,只需添加MOD。对于b,添加(1 + n * leave / MOD) * MOD

注意确保中间值不会溢出。n*leave如果大到足以包装正在使用的整数类型,则go 中的代码可能会给出错误的结果。

于 2016-12-06T02:35:07.287 回答
0

我不知道去,但这里有一些可能性:

b := adler >> 16 change to b := (adler >> 16) & 0xffff

b = (b - n*leave - 1 + a) % MOD ... what happens if expression in () is negative?

return b<<16 | a ... check operator precedence; (b<<16)|a or b<<(16|a) ?

32-bit machine or 64-bit?
于 2016-12-06T00:28:15.243 回答