google了一下,看到了这样一篇文章,注意到文中关于补码来历的描述,可以总结如下:
- 计算机里面,只有加法器,没有减法器,所有的减法运算,都必须用加法进行。
- 用补数代替原数,可把减法转变为加法。出现的进位就是模,此时的进位,就应该忽略不计。
- 二进制下,有多少位数参加运算,模就是在 1 的后面加上多少个 0。
- 补码就是按照这个要求来定义的:正数不变,负数即用模减去绝对值。
补充解释一下“模”的概念(不准确):
考虑时钟上时间的计算,假设现在时针指向数字3,若问“6小时前时针指向的数字是几”,则可以:
1. 将时针逆时针拨动6格。
2. 将时针顺时针拨动12 - 6 = 6格。
两者的结果是一样的。这里称12为“模”。
故有 3时 - 6个小时 = 3时 + (12 - 6个小时),这里可以看到将减法转换成加法的过程,即“加上模减去绝对值的差”。
所以,假设模是10,有效位数为1,当我们计算 9 - 7 的时候:
9 - 7 => 9 + (10 - 7) = 12,去掉最高的位后,得到2,这是正确的结果。
作者的意思是说,计算机里面所有数都以补码形式保存,加减运算都是补码之间的加法运算。然后作者提出了一个我之前没听过的观点:
补数 和 补码的定义式 里面,根本就没有什么符号位。这最高位的1、0是自然出现的,并不是由人来规定的。
的确,符号位在补码运算里面是“模”,本身并不带符号的意义。因为计算机将加法转换成加上一个“负数”,而负数又以补码的形式表现。补码比源码多一位,从这多出来的一位可以推断出原来数字的正负号,所以成为了符号位。也可以这样认为,留出一位(不全部占满)的原因是要用“模”来表示正负数。
也就是说,不是特意留出一个符号位,用1和0来表示正负号。而是补码运算可以用最高位来表示正负,所以符号位诞生了。
那么为什么-128的补码是10000000?可以这样理解。-128是一个负数,所以它的补码是它的“模”减去它的绝对值,即:
100000000 - 10000000 = 10000000
那么为什么负数补码等于源码的反码加一呢?可以这样推导:
100000000 - 10000000
= (11111111 + 00000001) - 10000000
= 11111111 - 10000000 + 1
= 01111111 + 1 //反码加一
= 10000000
由此我们得知,在计算机里面所有的数字都以补码形式存储。127存成01111111,-127存成11111111,算减法就变成算加法了,尽管你看到的是“-”号。
数字在自然界中抽象出来的时候,一棵树,两只猪,是没有正数和负数的概念的
计算机保存最原始的数字,也是没有正和负的数字,叫没符号数字
如果我们在内存分配4位(bit)去存放无符号数字,是下面这样子的
<img src="http://www.itdaan.com/imgs/0/6/9/0/14/e643837089991b67777b09126a30f45d.jpe" data-rawwidth="192" data-rawheight="165" class="content_image" width="192">
后来在生活中为了表示“欠别人钱”这个概念,就从无符号数中,划分出了“正数”和“负数”
正如上帝一挥手,从混沌中划分了“白天”与“黑夜”
为了表示正与负, 人们发明了"原码",把生活应该有的正负概念,原原本本的表示出来
把左边第一位腾出位置,存放符号,正用0来表示,负用1来表示
<img src="http://www.itdaan.com/imgs/0/5/5/9/47/edabc5169e14daecc4cc853919978a98.jpe" data-rawwidth="210" data-rawheight="218" class="content_image" width="210">
但使用“原码”储存的方式,方便了看的人类,却苦了计算机
<img src="http://www.itdaan.com/imgs/3/2/0/1/38/918677803b624c21254274660c37fad0.jpe" data-rawwidth="207" data-rawheight="49" class="content_image" width="207">
我们希望 (+1)和(-1)相加是0,但计算机只能算出0001+1001=1010 (-2)
这不是我们想要的结果 (╯' - ')╯︵ ┻━┻
另外一个问题,这里有一个(+0)和(-0)
为了解决“正负相加等于0”的问题,在“原码”的基础上,人们发明了“反码”
“反码”表示方式是用来处理负数的,符号位置不变,其余位置相反
<img src="http://www.itdaan.com/imgs/9/0/8/8/55/ab4ebd52fa22819377033ca32d5fc041.jpe" data-rawwidth="402" data-rawheight="250" class="content_image" width="402">
当“原码”变成“反码”时,完美的解决了“正负相加等于0”的问题
过去的(+1)和(-1)相加,变成了0001+1110=1111,刚好反码表示方式中,1111象征-0
人们总是进益求精,历史遗留下来的问题—— 有两个零存在,+0 和 -0
我们希望只有一个0,所以发明了"补码",同样是针对"负数"做处理的
"补码"的意思是,从原来"反码"的基础上,补充一个新的代码,(+1)
我们的目标是,没有蛀牙(-0)
<img src="http://www.itdaan.com/imgs/4/6/9/5/96/a506af476e1a23d0047532132a3101ac.jpe" data-rawwidth="527" data-rawheight="271" class="origin_image zh-lightbox-thumb" width="527" data-original="https://pic3.zhimg.com/d3617d2ceb02f6129c9b41361804cbe2_r.png">有得必有失,在补一位1的时候,要丢掉最高位 有得必有失,在补一位1的时候,要丢掉最高位
我们要处理"反码"中的"-0",当1111再补上一个1之后,变成了10000,丢掉最高位就是0000,刚好和左边正数的0,完美融合掉了
这样就解决了+0和-0同时存在的问题
另外"正负数相加等于0"的问题,同样得到满足
举例,3和(-3)相加,0011 + 1101 =10000,丢掉最高位,就是0000(0)
同样有失必有得,我们失去了(-0) , 收获了(-8)
以上就是"补码"的存在方式
结论:保存正负数,不断改进方案后,选择了最好的"补码"方案
计算机保存最原始的数字,也是没有正和负的数字,叫没符号数字
如果我们在内存分配4位(bit)去存放无符号数字,是下面这样子的
<img src="http://www.itdaan.com/imgs/0/6/9/0/14/e643837089991b67777b09126a30f45d.jpe" data-rawwidth="192" data-rawheight="165" class="content_image" width="192">
后来在生活中为了表示“欠别人钱”这个概念,就从无符号数中,划分出了“正数”和“负数”
正如上帝一挥手,从混沌中划分了“白天”与“黑夜”
为了表示正与负, 人们发明了"原码",把生活应该有的正负概念,原原本本的表示出来
把左边第一位腾出位置,存放符号,正用0来表示,负用1来表示
<img src="http://www.itdaan.com/imgs/0/5/5/9/47/edabc5169e14daecc4cc853919978a98.jpe" data-rawwidth="210" data-rawheight="218" class="content_image" width="210">
但使用“原码”储存的方式,方便了看的人类,却苦了计算机
<img src="http://www.itdaan.com/imgs/3/2/0/1/38/918677803b624c21254274660c37fad0.jpe" data-rawwidth="207" data-rawheight="49" class="content_image" width="207">
我们希望 (+1)和(-1)相加是0,但计算机只能算出0001+1001=1010 (-2)
这不是我们想要的结果 (╯' - ')╯︵ ┻━┻
另外一个问题,这里有一个(+0)和(-0)
为了解决“正负相加等于0”的问题,在“原码”的基础上,人们发明了“反码”
“反码”表示方式是用来处理负数的,符号位置不变,其余位置相反
<img src="http://www.itdaan.com/imgs/9/0/8/8/55/ab4ebd52fa22819377033ca32d5fc041.jpe" data-rawwidth="402" data-rawheight="250" class="content_image" width="402">
当“原码”变成“反码”时,完美的解决了“正负相加等于0”的问题
过去的(+1)和(-1)相加,变成了0001+1110=1111,刚好反码表示方式中,1111象征-0
人们总是进益求精,历史遗留下来的问题—— 有两个零存在,+0 和 -0
我们希望只有一个0,所以发明了"补码",同样是针对"负数"做处理的
"补码"的意思是,从原来"反码"的基础上,补充一个新的代码,(+1)
我们的目标是,没有蛀牙(-0)
<img src="http://www.itdaan.com/imgs/4/6/9/5/96/a506af476e1a23d0047532132a3101ac.jpe" data-rawwidth="527" data-rawheight="271" class="origin_image zh-lightbox-thumb" width="527" data-original="https://pic3.zhimg.com/d3617d2ceb02f6129c9b41361804cbe2_r.png">有得必有失,在补一位1的时候,要丢掉最高位 有得必有失,在补一位1的时候,要丢掉最高位
我们要处理"反码"中的"-0",当1111再补上一个1之后,变成了10000,丢掉最高位就是0000,刚好和左边正数的0,完美融合掉了
这样就解决了+0和-0同时存在的问题
另外"正负数相加等于0"的问题,同样得到满足
举例,3和(-3)相加,0011 + 1101 =10000,丢掉最高位,就是0000(0)
同样有失必有得,我们失去了(-0) , 收获了(-8)
以上就是"补码"的存在方式
结论:保存正负数,不断改进方案后,选择了最好的"补码"方案