前言
本文你将会知道JS中的按位非 (~),什么是原码、反码、补码,负数如何在计算机中表示,以及为什么要使用补码。
起因是在复习运算符优先级的时候,发现了按位非(~)。
以下是MDN官方给的示例:
const a = 5; // 00000000000000000000000000000101
const b = -3; // 11111111111111111111111111111101
console.log(~a); // 11111111111111111111111111111010
// expected output: -6
console.log(~b); // 00000000000000000000000000000010
// expected output: 2
有点蒙,为什么~a等于-6?真是“基础不牢,地动山摇”,所以有了本文。
我们知道计算机的数据存储是以0和1来表示的。那么为了能完整表示数据,就有了原码、反码、补码。
在计算机中,存储和计算都是以补码来完成的。
下面我们来看一下什么是原码、反码、补码:
1. 原码
负数在二进制中的表示是,最高位是1
如-5:
1000 0101 (原码)
注意,无论8位表示还是多少位,负数最高位都应该是1。
2. 反码
对于正数,反码与原码一致。
对于负数,原码按位取反(最高位不变)。
如-5:
1111 1010 (反码)
3. 补码
对于正数,补码与原码一致。
对于负数,补码要在反码的基础上加1。
如-5:
1111 1011 (补码)
4. 计算
刚刚我们说了计算机的存储和计算都是以补码来完成的。
那么为什么要使用补码?
在论述之前,我们需要知道计算机只要加运算,要通过操作负数来完成减运算。
例如:5 - 5
(1)如果使用原码,有以下结果:
0000 0101 (5原码)
1000 0101 (-5原码)
1000 1010 (-10)
明显不对
(2)如果使用反码,有以下结果:
0000 0101 (5反码)
1111 1010 (-5反码)
1111 1111 (-0反码)
为方便观看,将反码结果转成原码,有1000 0000(-0原码)。似乎结果是正确的?-0不就是0吗?但是我们要知道在计算机中0还有其它表示0000 0000。这样一来就有了两个二进制表示0,这样就乱套了。
(3)如果使用补码,有以下结果:
0000 0101 (5补码)
1111 1011 (-5补码)
10000 0000 (0)
我们讨论的是8位的计算,结果得到了9位,这里就可以直接的舍弃第九位。那么久得到0000 0000,记过为0,完全正确。
所以这就是使用补码的原因。
5. 总结
回到MDN官方示例
const a = 5; // 00000000000000000000000000000101
console.log(~a); // 11111111111111111111111111111010
// expected output: -6
按位非就是将5的补码的每一位进行反转,从而得到反码。
下面展示一下5的二进制反转后,是如何计算出-6的,切换8位,如下:
1111 1010(补码)
-1得到:
1111 1001(反码)
取反得到最后结果:
1000 0110(原码)
110就是6了
参考:
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Bitwise_NOT