i & 0xFF vs i % 256 带负数
i & 0xFF vs i % 256 with negative numbers
我想知道为什么 (i % 256) 和 (i & 0xFF) 如果 i 为 0 或正数则相同,但如果 i 为负数则完全不同。
用于测试的代码:
i = -100
while (i < 100) {
if (!((i & 0xFF) == (i % 256))) console.log(i);
i++;
}
这是为什么?
先总结一下运算符:
&
是按位与运算符。
- 例如,对于正 4 位数,
0110 (6)
0100 (4) &
-------
0100 (4)
- 例如,对于负 4 位(2 的补码)数,
1010 (-6)
1100 (-4) &
-------
1000 (-8)
%
是模运算符。 x % y
大致意思是 x - parseInt(x / y) * x
- 例如,对于正数,
8 % 3 === 2
- 例如,对于负数,
-8 % 3 === -2
现在回答问题
为了我们的解释,让我们简单地使用 i & 0b0011 (3)
和 1 % 0b0100 (4)
而不是 255 和 256。这对于这个问题的目的是等价的,因为 256
在二进制中是 10000...
和 0xff
在二进制中是 01111...
.
- 对于小于 4 的正数:
i & 0b0011
和 1 % 0b0100
都将 return i
.
2 % 4 = 4
0010 (2)
0011 (3) &
-------
0010 (2)
- 对于大于或等于 4 的正数:
i & 0b0011
和 1 % 0b0100
,
7 % 4 = 3
0111 (7)
0011 (3) &
-------
0011 (3)
出现这种情况是因为这些操作不同,所以你不会问为什么2+2
等于2*2
,而是2+3
!= 2*3
,对吧?
无论如何,主要问题是数字的按位表示不是1:1关系,不同的数字可以有相同的表示(检查Signed versus Unsigned Integers。了解一些细节)
所以,%
模运算符取除法的余数并且它有符号,所以
console.log(-100 % 256); // shows -100 which is true
按位 &
与物理位一起工作,它不理解符号位集,因此,如果我们查看 -100 的按位表示 - 它是 10011100
同时它是156
的按位表示
console.log((0b10011100)); // unsigned 156
console.log((0b10011100 << 24 >> 24)); // signed -100
负数的表示形式为two's complement,即-100
的二进制表示为
(2**32 - 100).toString(2) = 11111111111111111111111110011100
加上0xff
得到10011100
,也就是156
.
取模运算%
定义为
IF a % b == r THEN a == b * q + r for some q
总是有q
和r
两种选择,有正余数和负余数。例如,使用 7 % 3
、
7 = 3 * 2 + 1
7 = 3 * 3 - 2
负数也一样,-7 % 3
:
-7 = 3 * (-2) - 1
-7 = 3 * (-3) + 2
对于正数,所有语言都选择正余数。对于负数,选择因语言而异。例如在 python 中,余数总是正数,所以
-7 % 3 = 2 # python
Javascript 取负余数,所以在 JS
-7 % 3 = -1 // javascript
同样,对于-100 % 256
-100 = 256 * ( 0) - 100
-100 = 256 * (-1) + 156
在python中,余数是156
(因此匹配& FF
),在javascript中是-100
。
我想知道为什么 (i % 256) 和 (i & 0xFF) 如果 i 为 0 或正数则相同,但如果 i 为负数则完全不同。
用于测试的代码:
i = -100
while (i < 100) {
if (!((i & 0xFF) == (i % 256))) console.log(i);
i++;
}
这是为什么?
先总结一下运算符:
&
是按位与运算符。
- 例如,对于正 4 位数,
0110 (6)
0100 (4) &
-------
0100 (4)
- 例如,对于负 4 位(2 的补码)数,
1010 (-6)
1100 (-4) &
-------
1000 (-8)
%
是模运算符。 x % y
大致意思是 x - parseInt(x / y) * x
- 例如,对于正数,
8 % 3 === 2
- 例如,对于负数,
-8 % 3 === -2
现在回答问题
为了我们的解释,让我们简单地使用 i & 0b0011 (3)
和 1 % 0b0100 (4)
而不是 255 和 256。这对于这个问题的目的是等价的,因为 256
在二进制中是 10000...
和 0xff
在二进制中是 01111...
.
- 对于小于 4 的正数:
i & 0b0011
和1 % 0b0100
都将 returni
.
2 % 4 = 4
0010 (2)
0011 (3) &
-------
0010 (2)
- 对于大于或等于 4 的正数:
i & 0b0011
和1 % 0b0100
,
7 % 4 = 3
0111 (7)
0011 (3) &
-------
0011 (3)
出现这种情况是因为这些操作不同,所以你不会问为什么2+2
等于2*2
,而是2+3
!= 2*3
,对吧?
无论如何,主要问题是数字的按位表示不是1:1关系,不同的数字可以有相同的表示(检查Signed versus Unsigned Integers。了解一些细节)
所以,%
模运算符取除法的余数并且它有符号,所以
console.log(-100 % 256); // shows -100 which is true
按位 &
与物理位一起工作,它不理解符号位集,因此,如果我们查看 -100 的按位表示 - 它是 10011100
同时它是156
console.log((0b10011100)); // unsigned 156
console.log((0b10011100 << 24 >> 24)); // signed -100
负数的表示形式为two's complement,即-100
的二进制表示为
(2**32 - 100).toString(2) = 11111111111111111111111110011100
加上0xff
得到10011100
,也就是156
.
取模运算%
定义为
IF a % b == r THEN a == b * q + r for some q
总是有q
和r
两种选择,有正余数和负余数。例如,使用 7 % 3
、
7 = 3 * 2 + 1
7 = 3 * 3 - 2
负数也一样,-7 % 3
:
-7 = 3 * (-2) - 1
-7 = 3 * (-3) + 2
对于正数,所有语言都选择正余数。对于负数,选择因语言而异。例如在 python 中,余数总是正数,所以
-7 % 3 = 2 # python
Javascript 取负余数,所以在 JS
-7 % 3 = -1 // javascript
同样,对于-100 % 256
-100 = 256 * ( 0) - 100
-100 = 256 * (-1) + 156
在python中,余数是156
(因此匹配& FF
),在javascript中是-100
。