首页 > 解决方案 > 我需要 ((2^32-1) << 0) 的解释(和可能的解决方法)导致 Javascript 中的 -1

问题描述

编辑:最后的解释。

我试图使用 Uint32Array 实现一个 64 位整数类,并在两个 uint32 成员上执行按位运算。我很快发现,就我对规范的理解而言,按位运算返回一个有符号的 32 位整数。最初我希望 Uint32Array 只会处理符号位,但事实并非如此。

我尝试围绕符号问题进行编码,但我被困在我根本无法理解的事情

var a = (Math.pow(2, 32)-1); //set a to uint32 max value

到目前为止,一切都很好。

a.toString(2);// gives "11111111111111111111111111111111", as expected

然而:

(a << 0);             // gives "-1"
(a >> 1);             // gives "-1"
(a << 0) == (a >> 1); // evaluates to true

即使 JS 按位运算将数字转换为有符号的 32 位整数,向右移动 1 的 32 个集合位也不应该是 -1。或者他们应该?移动 0 位的非零数是否应该等于移动 1 位?这是一个错误吗?我是否遇到了未定义的行为?

通常类似问题的答案与签名的 32 位转换有关,但我看不出这应该如何导致这种行为。

EDIT2,解释:我困惑的原因是对负数如何用二进制表示的根本误解。虽然第一位实际上是符号位,1 表示负数,0 表示正数,但正如我假设的那样,其余位不仅用于存储 abs()。

带符号的 4 位示例: 0111等于+7. 1111不等于-7等于-1。我们如何以消极的方式结束?因为的二进制补码是。要获得一个数字的二进制补码,翻转所有位并加一个: -> -> 。11110001111100000001

现在我知道了,理解11..11 << 0存在-1很容易。它与我的 4bit 示例完全相似。11..11 >> 1现在-1也是完全可以预料的。有符号的右移>>是 1 填充,所以11..11 >> 1仍然11..11是仍然-1

我暂时保留它,因为我当然不是唯一一个误解二进制有符号整数表示的人。感谢大家的时间。

标签: javascriptbit-shiftsignunsigned-integer

解决方案


即使 JS 按位运算将数字转换为有符号的 32 位整数,向右移动 1 的 32 个集合位也不应该是 -1。或者他们应该?移动 0 位的非零数是否应该等于移动 1 位?这是一个错误吗?我是否遇到了未定义的行为?

这是正常的、预期的和定义的。是的,他们应该这样做。

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Right_shift是你使用的,它的描述是这样的:

右移运算符 (>>) 将第一个操作数向右移动指定的位数。右移的多余位被丢弃。最左边位的副本从左边移入。由于新的最左边的位与前一个最左边的位具有相同的值,因此符号位(最左边的位)不会改变。因此名称为“符号传播”。

因此,如果您有 32 位 1,则在应用右移 1 后,您将有 32 位 1。

它是 32 位宽的事实在规格中,https://tc39.es/ecma262/

6.1.6.1.10 Number::signedRightShift ( x, y )
[...]
4. 返回执行 lnum 符号扩展右移 shiftCount 位的结果。传播最高有效位。结果是一个有符号的 32 位整数。

(同样,<<产生 32 位有符号整数)


推荐阅读