【问题标题】:Why in Java (high + low) / 2 is wrong but (high + low) >>> 1 is not?为什么在 Java 中 (high + low) / 2 是错误的,但 (high + low) >>> 1 不是?
【发布时间】:2021-06-27 23:04:59
【问题描述】:

我了解>>> 修复了溢出:当添加两个大的正长时,您最终可能会得到一个负数。有人能解释一下这种按位移位如何神奇地解决溢出问题吗?它与>> 有何不同?


我的怀疑:我认为这与 Java 使用二进制补码这一事实有关,因此如果我们有额外的空间,溢出是正确的数字,但因为我们没有,所以它变成了负数。因此,当您移位并用零填充时,由于二进制补码,它会神奇地固定。但我可能是错的,有位头脑的人必须确认。 :)

【问题讨论】:

标签: java bit-manipulation bitwise-operators binary-search


【解决方案1】:

简而言之,(high + low) >>> 1 是一种使用未使用的符号位对非负数进行正确平均的技巧。


highlow 都是非负数的假设下,我们确定最高位(符号位)为零。

所以highlow 实际上都是 31 位整数。

high = 0100 0000 0000 0000 0000 0000 0000 0000 = 1073741824
low  = 0100 0000 0000 0000 0000 0000 0000 0000 = 1073741824

当您将它们加在一起时,它们可能会“溢出”到顶部。

high + low =       1000 0000 0000 0000 0000 0000 0000 0000
           =  2147483648 as unsigned 32-bit integer
           = -2147483648 as signed   32-bit integer

(high + low) / 2   = 1100 0000 0000 0000 0000 0000 0000 0000 = -1073741824
(high + low) >>> 1 = 0100 0000 0000 0000 0000 0000 0000 0000 = 1073741824
  • 作为带符号的 32 位整数,溢出并翻转为负数。因此(high + low) / 2 是错误的,因为high + low 可能是负数。

  • 作为无符号 32 位整数,总和是正确的。只需将其除以 2。

当然,Java 不支持无符号整数,所以我们最好除以 2(作为无符号整数)是逻辑右移 >>>

在具有无符号整数的语言(例如 C 和 C++)中,它变得更加棘手,因为您的输入可以是完整的 32 位整数。一种解决方案是:low + ((high - low) / 2)


最后列举一下>>>>>/的区别:

  • >>> 是逻辑右移。它用零填充高位。
  • >> 是算术右移。它用原始顶部位的副本填充其上部。
  • / 是除法。

数学上:

  • x >>> 1x 视为无符号整数并将其除以 2。它向下取整。
  • x >> 1x 视为有符号整数并将其除以 2。它向负无穷大舍入。
  • x / 2x 视为有符号整数并将其除以 2。它向零舍入。

【讨论】:

  • 不确定何时使用 >>> 以及何时使用 >>。我认为在这种情况下,我们选择 >>> 来修复可能的溢出(符号位溢出)问题。在 >> 和 >>> 之间选择的经验法则是什么?
  • >>> 是逻辑右移。它用零填充高位。 >> 是算术右移。它用原始高位的副本填充高位。从数学上讲,>>> 1 将数字视为无符号数并除以二舍入。 >> 1 将数字视为有符号并向下舍入到负无穷大。 / 2 将数字视为有符号并向零舍入。
  • 很好,答案永远不会过时。
【解决方案2】:

它用零填充最高位而不是用​​符号填充它们。

int a = 0x40000000;
(a + a)  /  2 == 0xC0000000;
(a + a) >>> 1 == 0x40000000;

【讨论】:

  • @JohnPristine:嗯,2 的补码 CPU 以完全相同的方式对有符号和无符号整数执行加法......唯一的区别在于 / 2。所以到目前为止,一切都是正确的。显然,由于有足够的位来表示和,所以有足够的位来表示商,>>> 1 就是这样做的。 / 2 错误的唯一原因是它试图纠正符号,显然 >>> 1 执行按位右移,这与无符号除以 2 相同......因此它必须 正常工作。我不确定这是否回答了您的问题...
  • 我的陈述是否正确:“我认为这与 Java 使用两个赞美有关,因此如果我们有额外的空间,溢出是正确的数字,但因为我们没有它变成负数。所以当你用零换档和划桨时,它会神奇地得到修复”?
【解决方案3】:

我建议阅读 Joch Bloch 的 http://googleresearch.blogspot.com/2006/06/extra-extra-read-all-about-it-nearly.html#!/2006/06/extra-extra-read-all-about-it-nearly.html 关于高和低的文章

“我为JDK编写的二进制搜索版本包含相同的错误。最近它破坏了某人的报告给Sun 计划,在等待了九年左右之后。”

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-08-13
    • 2021-08-07
    • 2021-09-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多