原码、反码、补码都是二进制表示数的方式
原码
原码:首位为符号位,0表示整数,1表示负数,其余位表示数值,例如0011表示+3,而1011表示-3。
优点:符合人类阅读习惯,无论正数负数都能马上读出来
缺点:计算机做运算的时候不会把符号位提取出来,然后单独计算数值位的,而是把整个数包括符号位一起参与运算,于是就导致了
问题一:0011+1011=1110(-6)的错误计算结果。
问题二:0存在着两种表示方式。正零和负零的问题0000(+0)、1000(-0)
思考:如果世界上只有加法,没有减法;只有正数,没有负数。那么计算机直接使用原码也就没这些问题了。只是,我们的世界也许会枯萎,也许会干涸,但绝不会让你省心。
为什么会有原码,反码,补码?(有人说这是缘木求鱼,好像还真是)
众所周知,计算机采用二进制表示数。那么具体如何表示其中也是有学问的。原码是人类最直观想到的表现方式,但原码有缺点,于是产生了反码,也有缺点,最后产生了补码,相对完美,于是计算机系统大多使用补码来存储二进制数。反码的作用主要是方便原码到补码的过渡,方便理解。
前面说过,如果只有加法和正数,那么使用原码没有什么问题。之所以出现反码、补码,正是为了解决负数和减法所带来的问题。所以正数的原码、反码、补码都是一样的。
反码
反码:对原码进行“符号位不变,数值位取反”
例如:-3原码表示1011,反码就是1100;+3和-3的和就是0011+1100=1111,而1111正是0(原码1000的补码就是1111)。于是解决了问题一。
优点:满足绝对值相同的正负数相加值为零。可以把减法转换成加法
缺点:依然还有两种0的表示方式:0000(+0)和1111(-0)。
备注:反码只是求补码的过渡,不要纠结,为什么要给它一个名字,明明没什么用啊。
思考:反码解决了问题一,但还有问题二呢?
于是出现了补码。
为什么需要把减法转换成加法运算?
因为计算机电路设计发展中,为了简化电路设计,只保留了加法器,去除了减法器,将减法运算转换成对负数的加法运算以达成运算目的。
补码
补码:对原码进行“符号位不变,数值位取反”,再加一得到(对反码+1得到)
例如:-3的反码是1100,其补码就是1101。
反码中的0有两种表示方式:0000(+0)和1111(-0),把1111+1并舍弃高位溢出得到0000,这和0000(+0)的表示方式是一样的。
优点:解决了问题二,此时0只剩一种表示方式0000。同反码一样可以把减法转换成加法。
缺点:对人不易读。也算不上缺点,毕竟是给计算机读的。
问题:为什么负数总比正数多一个?
对于4位的原码,表示范围是:
[0111,1111],也就是[7,6,5,4,3,2,1,+0,-0,-1,-2,-3,-4,-5,-6,-7],2^4个,2^3-1个正数,两个0,和2^3-1个负数。
对于4位的反码,尽管负数的表示不同,但范围和原码是一致的
[7,6,5,4,3,2,1,+0,-0,-1,-2,-3,-4,-5,-6,-7]。
对于4位的补码,表示范围是:
[7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8],2^4个,2^3-1个正数,一个0,2^3个负数。
这是由于补码的规则决定的,因为没有反码通过+1可以得到补码1000。所以就人为规定了1000就是-8。这就是为什么负数比正数多一个的原因。
例子:
Integer.MAX_VALUE 2147483647
原码:0111 1111 1111 1111 1111 1111 1111 1111
反码:0111 1111 1111 1111 1111 1111 1111 1111
补码:0111 1111 1111 1111 1111 1111 1111 1111
Integer.MAX_VALUE的相反数 -2147483647
原码:1111 1111 1111 1111 1111 1111 1111 1111
反码:1000 0000 0000 0000 0000 0000 0000 0000
补码:1000 0000 0000 0000 0000 0000 0000 0001
Integer.MIN_VALUE -2147483648
int类型的最小值比较特殊,因为绝对值2147483648原码32位的int无法表示会高位溢出。
原码:没有
反码:没有
补码:1000 0000 0000 0000 0000 0000 0000 0000(人为规定)
思考:为什么最小的负数补码不能通过原码转反码再转补码的方法推算出来?
计算机教材里面都是这样的呀!其实从“取反”这样的运算来看,不符合人类思维常用的推导过程,这其实更像是计算机处理器运算的过程。因为“取反”在计算机中是相对高效的运算。
实际上数学的推导过程是先根据数值位数求“模”,然后求负数相对于该“模”的最小正同余数,就是负数的补码了。具体推导过程网络搜。之所以变了样,应该就是算法计算机化导致的,类似于乘法的计算机实现通常是左移位运算,除法通常是右移位运算。而计算机教材展示的是计算机实现该算法的原理,而不是数学推导原理。所以我们可能犯了一个错误:把算法的计算机实现原理当成了数学原理来学习了。
思考:如果确实如此,那我前面的思考过程不就成了扯淡了么?
上述过程可能不是原始的数学推导过程,但确实是计算机的处理流程。是具体的实现方法,何来扯淡一说!但我的思考:“先有原码,解决问题一得到反码,解决问题二得到补码”的逻辑就显得扯淡了。毕竟目的就是得到补码,根本不存在这演化过程,那么反码就更没存在意义了。
原文地址:http://blog.51cto.com/weijiancheng/2097083