x & 0xffffffff 怎么能产生超过 32 位的呢?

How can x & 0xffffffff produce more than 32 bits?

简短总结

我希望 n & 0xffffffff 产生一个 32 位数字,而不是更多。但它产生了一个 64 位数字。为什么?

详情

在 Android (Java) 应用程序中,我有以下代码行:

hash = ((hash ^ b) * FNV_PRIME) & 0xffffffff;

在这一步之后记录 hash 的值时,我得到像 0x811d68ec0c35c40x342d586144387f57 等值,显然超过 32 位可以容纳。它们是 64 位数字。

hashlong 类型。我可以提供有关 bFNV_PRIME 的更多详细信息,但这似乎与问题无关。无论 ((hash ^ b) * FNV_PRIME) 的值是什么,当我们将它与 0xFFFFFFFF 一个 32 位数字按位与运算时,除了最低有效的 32 位之外,我们应该以全零结束。对吧?

中间结果的 intlong 数据类型之间是否有隐含的含义,并且可能使用高位表示负数?

好的,我似乎找到了解决办法。我将猜测它为何起作用。如果有人能对此有更多的了解,我会很高兴听到。

修复:在 & 右侧的十六进制文字中添加 L 以将其标记为 long:

hash = ((hash ^ b) * FNV_PRIME) & 0xffffffffL;

我对此进行了测试并且有效:代码现在仅生成 32 位值。

那到底出了什么问题,为什么要解决这个问题?

& 的左侧是一个 long 值,因为 hash 是一个 long(FNV_PRIME 也是,但不应该无关紧要)。为了 & 完成它的工作,它需要一个相同类型的操作数。因此它会自动将右侧值 0xffffffffint 提升到 long。由于 Java 类型是有符号的,因此 0xffffffff 被解释为 -1,而 long 将被解释为 0xffffffffffffffff。所以上面的行最终相当于

hash = ((hash ^ b) * FNV_PRIME) & 0xffffffffffffffff;

这就是我最终从中获取 64 位值的方式。

当我改为创建正确的操作数 0xffffffffL 时,它已经是 long 并且不需要提升,所以它没有被解释为负数,因为高位。换句话说,0xffffffffL等同于0x00000000ffffffffL,所以没有设置高位。

这个故事的寓意是什么?

好吧,我需要帮助。一些想法:

  • 彻底理解 Java 如何在每次计算的每个中间阶段决定使用什么数据类型来表示数字。呃,这听起来很难,尤其是当大多数事情 "work fine" 大多数时候。

  • 只是得过且过,直到出现问题为止,然后使用调试器进行更详细的跟踪,直到找到问题为止。这是假设如果程序失败,它会在开发人员手中失败。

  • 良好且彻底的单元测试。 :-) 不确定我是否会设计一个可以检测到这个问题的测试,例如断言我的函数的 return 值不超过 32 位长的测试。

  • 请注意 IDE 中的编译器警告。直到游戏后期我才注意到它,但最终我看到 Android Studio 有一条警告说:

    'hash = ((hash ^ b) * FNV_PRIME) & 0xffffffff'可以替换为'hash = ((hash ^ b) * FNV_PRIME)'

如果我早点读到那篇文章,我会感到非常困惑,但它会给我一个很好的问题线索。