为什么我的滚动 adler32 校验和在 go 中不起作用?(模算术)

use*_*678 2 checksum go modulo adler32

我正在 go 中实现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
}
Run Code Online (Sandbox Code Playgroud)

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

令我困惑的是,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
Run Code Online (Sandbox Code Playgroud)

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

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

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

Mar*_*ler 5

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.

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

  • 哇,校验和的作者本人给出了答案!非常感谢您的清晰解释。 (3认同)