原码:最简单的机器数,例如前文提到从 +1110 和 -1110 转换得到的 0000, 1110 和 1000, 1110 就是原码表示法,所以原码在进行数字运算时会存在前文提到的效率问题
反码:一般认为是原码和补码转换的中间过渡
补码:补码才是解决机器数的运算效率的关键,在计算机中所有 "整型类型" 的负数都会使用补码表示法
Javascript,可以使用 Number.toString(2) 来查看一个数的二进制值(字符串),但是如果是负数,并不是简单的补码表现形式,而是用数字绝对值的标准二进制代码(原码)前面加负号的形式输出:如 (-18).toString(2) === '-10010' ,这样做,似乎是 ECMAScript 想让开发者不必关心它们的用法
求负数补码的步骤:
得到这个负数的原码,如 -14 的原码是:1000 1110
保留符号位不变,将数值位取反,得到反码:1111 0001
将反码 + 1,得到补码:1111 0010
所以,拿到一个二进制数,其实首先应该问的是这个二进制是用何种形式表示的,是原码码值还是补码码值,然后在进行翻译。但是在实际中,负数一般都是用的补码码值,而正数的原码和补码又是一致的,所以我们在看到一个二进制数的时候,默认其是补码的表现形式。
所以在上面的两个负数的计算中,我们用补码进行计算的过程是:
1111, 0010 + 1111, 1111 = 1111, 0001 // (-14) + (-1) = (-15) 正确
^ ^ ^
符号位 符号位 符号位(最高位的 1 溢出)
由此,使用补码表示法后,有符号机器数加法运算就只是纯粹的加法运算,不会因为符号的正负性而采用不同的计算方法,也不需要减法运算,简化了电路设计
同时,除了消除减法运算外,补码表示法还实现了 "0" 的机器数的唯一性:
在原码表示法中,"+0" 和 "-0" 都是合法的,而在补码表示法中 "0" 只有唯一的机器数表示,即 "0000, 0000" 。换言之补码能够比原码多表示一个最小的负数 1000, 0000
三、补码背后的规律
具体与数学中的补数相关,这里简单举个例子来描述下规律:
时钟的例子
比如说,现在时钟的时针刻度指向 6 点,我们想让它指向 3 点,应该怎么做?
方法 1:逆时针地拨动 3 个点数,让时针指向 3 点,这相当于做减法运算 -3
方法 2:顺时针地拨动 9 个点数,让时针指向 3 点,这相当于做加法运算 +9
可以看到,对于时钟来说 -3 和 +9 竟然是等价的。
这是因为时钟只能 12 个小时,当时间点数超过 12 时就会自动丢失,所以 15 点和 3 点在时钟看来是都是 3 点。如果我们要在时钟上进行 6 - 3 减法运算,我们可以将 -3 等价替换为它的正补数 +9 后参与计算,从而将减法运算替换为 6 + 9 加法运算,结果都是 3
十进制的一个例子
如果计算 354365 - 95937,用补数的规律该怎么做?
354365 - 95937 // = 258428
= 354365 - (1000000 - 904063)
= 354365 - 1000000 + 904063 // 减整加补
= 258428
这里存在的问题是:为何是 1000000 ?这个 1000000 就相当于是时钟中的 12,是当前运算中的最大位数上限(周期之类的概念),这个数据似乎还和进制相关(这个有空再研究),所以实际上,运算的过程是:
354365 + 904063 = 1258428 = 258428
^
最高位 1 超出位数限制,直接丢弃
总结
补码的关键在于:找到一个与负数等价的正补数,使用该正补数代替负数,从而将减法运算替换为两个正数加法运算。
补码的出现与运算器的电路设计有关,从设计者的角度看,希望尽可能简化电路设计和计算复杂度。而使用正补数代替负数就可以消除减法器,实现简化电路的目的
四、位运算
位运算是基于二进制值的运算(补码表现形式)
Operator
Usage
Description
按位与
a & b
在 a,b 的位表示中,每一个对应的位都为 1 则返回 1,否则返回 0
按位或
a | b
在 a,b 的位表示中,每一个对应的位,只要有一个为 1 则返回 1,否则返回 0
按位异或
a ^ b
在 a,b 的位表示中,每一个对应的位,两个不相同则返回 1,相同则返回 0
按位非
~ a
反转被操作数的位
左移
a << b
将 a 的二进制串向左移动 b 位,右边移入 0
算术右移(有符号右移,其实就是保持符号位不变,数值位右移)
a >> b
把 a 的二进制表示向右移动 b 位,丢弃被移出的所有位
无符号右移(左边空出位用 0 填充)
a >>> b
把 a 的二进制表示向右移动 b 位,丢弃被移出的所有位,并把左边空出的位都填充为 0
五、位运算在 Javascript 中的应用
移步:位运算符在 Javascript 中的运用