二进制世界的秘密( 二 )


上述例子中还是以 39 为例,我们先把十进制的39 转换为二进制的 0010 0111,然后向左移位 <<一个字节,也就变成了0100 1110,那么再把此二进制数转换为十进制数就是上面的78, 十进制的78 竟然是 十进制39 的2倍关系 。我们在让0010 0111左移两位,也就是1001 1100,得出来的值是 156,相当于扩大了四倍!
因此你可以得出来此结论,左移相当于是数值扩大的操作,那么右移 >>呢?按理说右移应该是缩小 1/2,1/4 倍,但是39 缩小二倍和四倍不就变成小数了吗?这个怎么表示呢?请看下一节
 
便于计算机处理的补数刚才我们没有介绍右移的情况,是因为右移之后空出来的高位数值,有 0 和 1 两种形式 。要想区分什么时候补0什么时候补1,首先就需要掌握二进制数表示负数的方法 。
二进制数中表示负数值时,一般会把最高位作为符号来使用,因此我们把这个最高位当作符号位 。符号位是 0 时表示正数,是 1 时表示负数 。那么 -1 用二进制数该如何表示呢?可能很多人会这么认为:因为 1 的二进制数是0000 0001,最高位是符号位,所以正确的表示 -1 应该是1000 0001,但是这个答案真的对吗?
【二进制世界的秘密】计算机世界中是没有减法的,计算机在做减法的时候其实就是在做加法,也就是用加法来实现的减法运算 。比如 100 - 50 ,其实计算机来看的时候应该是 100 + (-50),为此,在表示负数的时候就要用到二进制补数,补数就是用正数来表示的负数 。
为了获得补数,我们需要将二进制的各数位的数值全部取反,然后再将结果 + 1 即可,先记住这个结论,下面我们来演示一下 。

二进制世界的秘密

文章插图
-1 取反过程
具体来说,就是需要先获取某个数值的二进制数,然后对二进制数的每一位做取反操作(0 ---> 1 , 1 ---> 0),最后再对取反后的数 +1 ,这样就完成了补数的获取 。
补数的获取,虽然直观上不易理解,但是逻辑上却非常严谨,比如我们来看一下 1 - 1 的这个过程,我们先用上面的这个 1000 0001(它是1的补数,不知道的请看上文,正确性先不管,只是用来做一下计算)来表示一下
二进制世界的秘密

文章插图
1 - 1 分析图
奇怪,1 - 1 会变成 130 ,而不是0,所以可以得出结论 1000 0001表示 -1 是完全错误的 。
那么正确的该如何表示呢?其实我们上面已经给出结果了,那就是 1111 1111,来论证一下它的正确性
二进制世界的秘密

文章插图
1 - 1 正确的分析图
我们可以看到 1 - 1 其实实际上就是 1 + (-1),对 -1 进行上面的取反 + 1 后变为 1111 1111, 然后与 1 进行加法运算,得到的结果是九位的1 0000 0000,结果发生了溢出,计算机会直接忽略掉溢出位,也就是直接抛掉 最高位 1 ,变为0000 0000 。也就是 0,结果正确,所以1111 1111表示的就是 -1。
所以负数的二进制表示就是先求其补数,补数的求解过程就是对原始数值的二进制数各位取反,然后将结果 + 1,
当然,结果不为 0 的运算同样也可以通过补数求得正确的结果 。不过,有一点需要注意,当运算结果为负的时候,计算结果的值也是以补数的形式出现的,比如 3 - 5 这个运算,来看一下解析过程
二进制世界的秘密

文章插图
3 - 5 的解析过程
3 - 5 的运算,我们按着上面的思路来过一遍,计算出来的结果是 1111 1110,我们知道,这个数值肯定表示负数,但是负数无法直接用十进制表示,需要对其取反+ 1,算出来的结果是 2,因为1111 1110的高位是 1,所以最终的结果是 -2 。
编程语言的数据类型中,有的可以处理负数,有的不可以 。比如 C语言中不能处理负数的 unsigned short类型,也有能处理负数的short类型 ,都是两个字节的变量,它们都有 2 的十六次幂种值,但是取值范围不一样,short 类型的取值范围是 -32768 - 32767 , unsigned short 的取值范围是 0 - 65536 。
仔细思考一下补数的机制,就能明白 -32768 比 32767 多一个数的原因了,最高位是 0 的正数有 0 ~ 32767 共 32768 个,其中包括0 。最高位是 1 的负数,有 -1 ~ -32768 共 32768 个,其中不包含0 。0 虽然既不是正数也不是负数,但是考虑到其符号位,就将其归为了正数 。


推荐阅读