- 计算机中,存储数据是使用一个数字的二进制的补码进行的。
- 为什么这么设计呢?这是一个好的问题。不知道你的大学老师有没有给你讲清楚。
- 我搞了一下,讲给你听:
- 首先,计算机是只能存储0和1的,并且计算机的0和1,都是用低电平、高电平表示。也就是想象一下,有这么几个坑,有的有电,有的没有电。要进行计算的时候,只能向这个坑里进行填充电压,比如放进去一个高电平,或者放进去一个低电平(相当于没放)。所以它是只进不出的貔貅。
- 所以说,计算机只能做加法,而不能做减法。
- 那么,我们要做减法的时候怎么操作呢,因为是有这个减法的数学概念的呀。人们就想了一个办法,把要减去的数字,表示成它的负数,这样,减法就变成了加上一个负数。关键,什么是负数呢?就是欠着。
- 比如我要减去75,那就等于加上一个负的75。就是说,我要还75块钱的债务。也可以麻烦一点就是我还你100块的债务,这时候,你是不是应该给我25再?好,先后顺序换一下,我要归还75元的债务,你先再借给我25,我资产先增加25,然后我一起还给你100,这样是不是就简单了?所以,我们就用 (-100 + 25 )这样的组合,去表示-75,这样能够解决问题,虽然显得有点麻烦,但实际生活中,这事儿还真不少。
- 那这样有什么好处呢?比如,我现在有80,我现在要还你75,应该怎么做? 首先,我现在假装还不起你。然后,我还再借款增资25,也就是80+25,这时候,我有多少? 105对不对,之前还借了75呢,25+75=100,总共要还100了,但是这回有了,还账吧,105-100=5.这就是最终结果,而过程中的+25,就是我们负债数字-75的补数。好处呢,就是凑了个整,本来80-75是挺难算的,但是105-100就容易了。对于人来说是这样,但是对于计算机来说,80-75,为啥要这么整呢,这样就避免了做减法,修改成85+25了。
- 那位说,这不还是做了减法吗?105不是减去了100么,也可以这么认为,我们约定,我现在就是要还钱。我总共有80元,不管怎么去借,最终都是要还的,那么最终的资产,肯定不会超过80元。我们把超过80元的105的获取的过程中,自动把100忽略掉就好了,怎么忽略呢,就是我们说好,只计算两位数的范围内的减法,减法是不可能比原来的数字大的,所以肯定不会发生进位。如果进位了,就忽略掉百位(第三位)数字即可。就是说,借可以但是不要借太多,比如借一个亿,没啥意义不是,我们只需要刚好比计算的数字大一位数的最小进位即可。最小进位的概念就是,只需要再加1,就发生进位的那个。
- 同样,计算机里边,虽然是用二进制表示,我们也可以这么干。
- 十进制中,从百位借一位,表示100. 二进制从百位借一位,表示4. 比如3 要 减 1,我们只需要3+3-4即可。结果就一样是2.后面那个3,就跟上面的25一样,就是找到我们要减去的那个数字的补数。规律就是,一个数字的补数,跟他自己相加,应该会发生进位。如果不考虑进位,那结果就是 0 。
- 而正数的加法,是不需要使用补数的,所以正数的补数,跟他自己是一样的。
- 那么,我们怎样快速找到一个任意数字的补数呢?
- 我们来看一个二进制数字: 1000 0000,他的补数就是比他位数多一个的数字,减去它自己:1 0000 0000 - 1000 0000 ,结果是 什么呢?思考,1000 0000 加上多少,才会刚好发生进位? 0111 1111 加上之后,刚好全是1,此时再加一个1,就会发生进位。那么,1000 0000 的补数,就是 (0111 1111 + 1),咦? 刚好,还是1000 0000没有变化啊?
- 好,这时候,认真思考一下,1000 0000 表示的是一个什么数字?如果是正数,那么正数的补数,就是他自己,没有变化是对的。如果它表示的是负数,那么,按照上面的规则计算,结果也是一样的,所以这就神奇了,这个数字的补数就是他自己,无论它表示正数,还是负数,结果都是他自己,而自然界中,只有一个数字可以这样说,那就是0,+0,-0,都是一样的0。所以,我们可以把最高位的1,看成是负数的代表,也就是符号位。
- 讲多了哈,回到补数上来,补数的获取方式,就是把一个二进制数字,对应是0的位置,全部补上1,对应是1的位置,补上0,这时候,两个数字相加,刚好所有位数全都是1,此时再 + 1,就发生最小进位了,也就是补数的概念了。
- 那么,我们把一个二进制数字对应是零的位置设为1,对应是1的位置设为0的操作,叫做取反码。刚好相反的码么。
- 而把这个反码加上1,就成为这个数字的补数。如果要减去这数字,只要加上他的补数,再忽略掉进位就好了。
- 所以,我们默认,计算机里的所有正数原样表示,而把计算机里边的所有负数,都用这个负数对应的正数的补码去表示,这样,要加上一个负数的时候,就只需要加上这个补码,忽略掉进位,就可以完成负数的加法了。
- 而要完成这个操作,又需要有一个借数字的范围,所以我们就需要有一个约定的数据类型,比如,java语言里边,int是使用4个字节去表示一个数字的,表示成二进制,就是32位的一个数字。
- 那么借数的时候,只需要从第33位(这一位实际并不存在,如果出现,就是溢出,会被自动忽略)借就好了。
- 总而言之,我们需要谨记: 计算机中的负数,全部使用其补码形式在内存中,用二进制记录。这样方便与其他正数相加。而正数就不需要使用补码了。
- 所以,负数的补码,等于其反码+1,正数不需要这些。
- 然后还相关的,就是存储范围。
- 比如1个字节,8个bit,0000 0000 —> 1111 1111 范围就是0 --> 255,如果需要表示负数那就要折半,正数负数各一半,也就是-128 ~ +128(注意-128 ~ +128是257个数字),那么,怎么办呢?大家知道二进制向右移一位,就表示除以二,那么最高位去掉,就剩下了两半,最高位是1,表示负数,最高位是0,表示正数(咦?这是-127~127啊)。
- 我们仔细来看看所有的负数,从1000 0000 - 11111 1111,全都是负数比较特殊的就是这个负零。 因为正数那一堆(0000 0000 - 0111 1111)里边已经有了一个0了,没有必要使用正零和负零同时表示一个相同的数字。而0000 0000 == 1000 0000也挺奇怪的不是?所以我们把1000 0000 定义为-128. 因为按照正数算,1000 0000 刚好是128,但是因为它的首位(符号位)又是1,所以它应该是负数。
- 所以,最终这个表示范围,是 (-128 ~ 127),呼 ~ ~ ,一切都完美了,不是吗?
版权归属:
火星人
本文链接:
http://localhost:8090/archives/java-yu-er-jin-zhi--zheng-fu-shu--fan-ma-bu-ma-de-zhi-shi-bu-lou
许可协议:
本文使用《署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)》协议授权
评论区