先看一下《高程》对于按位非的定义

按位非操作符由一个波浪线(~)表示,执行按位非的结果就是返回数值的反码。

然后《高程》上又说按位非的本质是:操作数的负值减一。

程序上的表现就是

console.log(~25)    //-26

还是以25这个数值为例子

1
2
3
var num1 = 25;              //二进制 0000 0000 0000 0000 0000 0000 0001 1001
var num2 = ~num1;           //二进制 1111 1111 1111 1111 1111 1111 1110 0110
alert(num2);                //-26

上面的例子我们无论如何也无法也25的反码和-26联系起来。

要解释这个,我得从计算机的 原码、反码、补码 说起

####原码

原码就是符号位加上真值的绝对值, 即用第一位表示符号, 其余位表示值. 比如如果是8位二进制:

[+1]原 = 0000 0001

[-1]原 = 1000 0001

####反码

正数的反码是其本身.

负数的反码是在其原码的基础上, 符号位不变,其余各个位取反.

[+1] = [00000001]原 = [00000001]反

[-1] = [10000001]原 = [11111110]反

####补码

正数的补码就是其本身

负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1. (即在反码的基础上+1)

[+1] = [00000001]原 = [00000001]反 = [00000001]补

[-1] = [10000001]原 = [11111110]反 = [11111111]补

关于原码、反码、补码 跟多的了解可以参看

原码, 反码, 补码 详解

有符号数的处理

原码、反码、补码 都是计算机对于有符号数的表示方式,后来补码因为解决了+0 和 -0 二进制表示不同的问题而被广泛运用。

所以现在计算机运算的时候都是使用数值的补码进行运算。那么~25 运算得到的其实是一串补码。

[1111 1111 1111 1111 1111 1111 1110 0110]补

[1111 1111 1111 1111 1111 1111 1110 0101]反

[1000 0000 0000 0000 0000 0000 0001 1010]原

-(2^4+2^3+2^1) = -26

这就是《高程》所说的『按位非的本质是:操作数的负值减一』的原理。