位AND OR和XOR如何处理负符号整数



我只是在bitwise operators上解决随机问题,并尝试各种其他组合来做个人笔记。不知怎么的,我就是想不出解决办法。

假设我想检查两个整数之间的按位AND,或者对~number和-负数(~num1&-num2)以及其他各种组合进行检查。然后我可以看到答案,但我还无法确定这是怎么发生的?

控制台:

console.log(25&3);输出1(我可以很容易地解决这个问题)。

console.log(-25&-3);输出-27.

类似

console.log(~25&~3);输出-28。

console.log(25&~3);输出-24。

console.log(~25&3);输出-2。

console.log(~25&-3);输出-28。

console.log(-25&-3);输出-28。

我知道"console.log(25&-3)">

25是11001
-3是11101(3=00011减号就像2s complimit+1)
AND-11001=25。

但当两个数字都为负数或与上述其他情况一起使用时,我无法使其以相同的方式工作。我也试过各种数字组合,不仅仅是这两种。但我解决不了这个问题。有人能解释一下我不能解决的问题中使用的二进制逻辑吗。

(我在SO上花了大约2个小时来寻找答案,在谷歌上又花了1个多小时,但我仍然没有找到答案)。

谢谢和问候。

JavaScript指定对整数执行按位运算,就好像它们存储在二进制补码表示法中一样。幸运的是,现在的大多数计算机硬件都使用这种表示法。

为了简洁起见,我将以下数字显示为8位二进制。它们在JavaScript中实际上是32位的,但对于原始问题中的数字,这并不能改变结果。然而,它确实让我们放弃了很多领先的部分。

console.log(-25 & -3); //outputs -27. How?

如果我们把整数写成二进制,我们分别得到(11100111&11111101)。把这些加在一起,你得到11100101,也就是-27。

在后面的例子中,您似乎可以交替使用NOT运算符(~)和否定(-)。在two的补码中不能这样做:~和-不是一回事~25是11100110,是-26,而不是-25。类似地,~3是11111100,它是-4,而不是-3。

但当我们把这些放在一起时,我们就可以计算出你给出的例子。

console.log(~25 & ~3); //outputs-28. How?

11100110&11111100=11100100,这是-28(而不是您所写的28)

console.log(25 & ~3);//outputs-24. How?

00011001&11111100=00011000,即24

console.log(~25 & 3);//outputs-2. How?

11100110&00000011=00000001,即2

console.log(~25 & -3);//outputs--28. How?

11100110&111111 01=11100100,即-28

console.log(-25 & ~3);//outputs--28. How?

11100111&11111100=11100100,即-28

理解这一点的真正关键是您并没有真正对整数使用逐位运算。您可以将它们用于特定大小的钻头袋,而这些钻头袋恰好可以方便地表示为整数。这是理解这里发生的事情的关键,因为你偶然发现了一个差异很重要的案例。

在计算机科学中,有一些特定的情况下,你可以以某种方式操纵比特包,巧合的是,会给出与你对数字进行特定数学运算相同的结果。但这只在特定情况下有效,它们要求你对你正在研究的数字做出某些假设,如果你的数字不符合这些假设,事情就会崩溃。

这也是Donald Knuth说"过早优化是万恶之源"的原因之一。如果你想使用逐位运算来代替实际的整数数学,你必须绝对确定你的输入实际上会遵循该技巧所需的假设。否则,当您开始使用这些假设之外的输入时,结果将开始看起来很奇怪。

32位整数的二进制字符串表示可以通过以下方式找到:

(i >>> 0).toString(2).padStart(32, '0')

两个二进制字符串的按位固定是直接的

有符号的32位二进制字符串的整数值是

parseInt(bitwiseAndString, 2)

如果字符串以"0"或开头

-~parseInt(bitwiseAndString, 2) - 1

如果它以"1"开始

把所有这些放在一起:

const tests = [
['-25', '-3'],
['~25', '-3'],
['25', '~3'],
['~25', '3'],
['~25', '~3'],
['-25', '~3']
]
const output = (s,t) => { console.log(`${`${s}:`.padEnd(20, ' ')}${t}`); }
const bitwiseAnd = (i, j) => {
console.log(`Calculating ${i} & ${j}`);
const bitStringI = (eval(i) >>> 0).toString(2).padStart(32, '0');
const bitStringJ = (eval(j) >>> 0).toString(2).padStart(32, '0');
output(`bit string for ${i}`, bitStringI);
output(`bit string for ${j}`, bitStringJ);
const bitArrayI = bitStringI.split('');
const bitArrayJ = bitStringJ.split('');
const bitwiseAndString = bitArrayI.map((s, idx) => s === '1' && bitArrayJ[idx] === '1' ? '1' : '0').join('');
output('bitwise and string', bitwiseAndString);
const intValue = bitwiseAndString[0] === '1' ? -~parseInt(bitwiseAndString, 2) - 1 : parseInt(bitwiseAndString, 2);
if (intValue === (eval(i) & eval(j))) {
console.log(`integer value: ${intValue} ✓`);
} else {
console.error(`calculation failed: ${intValue} !== ${i & j}`);
}
}
tests.forEach(([i, j]) => { bitwiseAnd(i, j); })

25 = 16+8+1 = 0b011001,我添加了另一个0数字作为符号数字。实际上,你至少会有8个二进制数字但两者的补码数学是一样的。要在6位2的补码中得到-25,您需要执行-25 = ~25 + 1=0b1001113=2+1=0b000011; -3 = ~3+1 = 0b111101

当你&两者时,你得到:

-25 = ~25 + 1=0b100111
&
-3 = ~3 + 1 = 0b111101
0b100101

最左边的位(符号位)被设置为负数。要找到它的负数,你可以颠倒这个过程,先减去1,然后做~

~(0b100101-1) = 0b011011

那是1+2+0*4+8+16 = 27,所以是-25&-3=-27

对于25&3,它是:

25 = 16+8+1 = 0b011001
&        ~3 = 0b111100
______________________
0b011000 = 24

对于~25&3,它是:

~25 =         0b100110
&        ~3 = 0b000011
______________________
0b000010 = 2

对于~25&amp-3,它是:

~25 =         0b100110
&      ~3+1 = 0b111101
______________________
0b100100 #negative
#find what it's a negative of:
~(0b100100-1) =~0b100011 = 0b011100 = 4+8+16 = 28
0b100100 = -28

-27中有6个二进制数字,因此您应该使用至少有那么多数字的数字。对于8位数字,我们有:

  • 00011001=25
  • 00000011=3
  • 00011011=27

和:

  • 11100111=-25
  • 111111 01=-3
  • 11100101=-27

现在-25&amp-3=-27,因为11100111&111111 01=11100101

最新更新