前言
聊补码的前因是由于昨晚做某W的笔试时碰到了一道题,给我干不会了:
十进制的-6.6875,转化为二进制补码是___________;(1位符号位,4位整数位,4位小数位)
点击查看答案
1.10010101
不会的同学就坐下看看吧;会的同学也可以看看,能否从我的理解里学到些什么。
正文
1. 为什么发明补码
简言之,是为了方便计算机对负数进行处理。不了解的可以去看看这篇文章。
2. 补码的计算方法
2.1 十进制整数求得二进制补码
判断该数的正负性:若为正,则该数的二进制原码就是二进制补码;若为负,则将该数的二进制原码的所有数据位(不包括符号位)取反后在最低位+1。
2.2 十进制纯小数求得二进制补码
同2.1。
2.3 十进制整数+小数求得二进制补码
同2.1。
但我要提出一个问题:为什么不能把整数和纯小数分开算,算完再合在一起呢?前言中那道题不是将整数位4位和小数位4位分得很清楚吗?
3. 补码的本质
要想解答2.3的这个问题,首先得先了解补码的本质到底是什么,即为什么补码的计算方式是如2.中所说的那样。
其实如果认真看了1.的朋友估计已经能理解一些了。补码(2's complement),正常中译过来应该叫"2的补"。
假设一个负数的二进制原码为x,数据位个数为k,那么补码的定义是:
-x = 2^k - x
因为对于计算机来说,要求就是时间复杂度和空间复杂度越低越好。而补码可以说是二进制表示负数的方法中最简洁的方法,因为它可以将+/-运算都看成+,不需要判断也不需要多封装一个减法器;而且节省出了-0的位置,能够多表示一位负数(如8位数中的-128)。
讲完真正的定义,接着来看二进制减法是如何做的:
假设一个负数x的8位二进制原码(4位整数位,4位小数位,没有包含符号位)为:10110110,那么接下来我
们用2^8 - x:
1 0000 0000
— 0 1011 0110
———————————
? ???? ????
我们会发现上式中需要借位,也就是
1 0000 0000 = 1111 1111 + 0000 0001
那么上式变为:
0 1111 1111 + 0 0000 0001
— 0 1011 0110
———————————
? ???? ???? + 0 0000 0001
1 - 1 = 0 , 1 - 0 = 1;我们发现,其实所谓的取反,就是拿一个相同位数并且全1的二进制数减去我们的原码x,而取反后加1,其实是最低位借位之后剩下的,要补上。
那么现在就知道为什么不能拿整数+小数分开算了,因为分开算补码之后再合并到一起就相当于:
1 0001 0000
— 0 1011 0110
———————————
? ???? ????
这会导致二进制补码存在+1偏置,当变量x在[-1,0)的范围内时,算出来的补码会与某个其他变量算出来的补码一致,导致多个值对应一个二进制补码的问题。