重新认识原码、反码和补码

前言

在计算机的世界里,所有的数据最终都以二进制的形式而存在.比如我们人类生活中使用的十进制数字可以按照一定的规则换算成二进制来存储.另外还有八进制,十六进制它们都能转化成二进制.

很多其他字符像标点符号,字母以及中文它们又是怎么在计算机中表示的呢?在计算机内部会存储一个码表,不同的二进制数字对应着唯一一个字符,比如 0100 0001 这串数字就表示大写字母 A.

那计算机又是怎么存储图片和视频的呢?图片在屏幕上有很多个像素点组成,而每个像素点都可以用二进制的数字来表示其颜色.比如4位的二进制数字来表示一个像素点,那么4位二进制数字共有8种组合,则该像素点也就能表示八种颜色.如果用一个字节(8位二进制数字)来表示一个像素点,那么每个像素点总共就有256种颜色的选择.像素点越多越密集,图片的颜色也就越丰富,最终展现的效果也越清晰.视频是由图片快速播放而形成的,原理类似.

从上面的概述可以看出来,计算机的底层仅仅使用只有0和1组合成的二进制数字就能表示万事万物,而二进制毕竟是一堆数字,是数字就能进行算术运算.我们当今庞大复杂的信息社会底层的运行都转化成为了一堆二进制数字的计算.由此可见,二进制的计算拥有着多么重要的战略地位.

 

进制转换

十进制转化成二进制

十进制整数转换为二进制整数采用"除2取余,逆序排列"法.用2整除十进制,得到商和余数;再用2除商,又会得到一个商和余数,如此进行,直到商小于1时为止,最后从下往上从最后的商开始逐步结合余数排列起来.十进制数173也就换算成了二进制数字010101101,最前面的0可以省略.如下:

                                            

                                             

二进制转换成十进制

  • 把二进制数首先写成加权系数展开式,然后按十进制加法规则求和。这种做法称为"按权相加"法
  • 例如 1101,最右边第一位对应的加权系数为2的零次方,第二位为2的一次方,第三位为2的二次方,依次类推
  • 每个数字乘以它的加权系数并相加得到的和就等于十进制数字的值了
  • 1*2^3 + 1*2^2 +0*2^1 + 1*2^0 = 8 + 4 + 0 + 1 = 13

 

原码

几十年以前,许多科学家在军事和航天领域需要进行大批量的大数计算,比如多少千万乘以多少千万之类.人工来演算这些数字不仅容易出错而且计算效率十分低效,计算机因此应运而生.它出生的目的就是为了帮人类快速计算出结果,假如现在我们需要创建一台计算机,我们给它输入十进制加法 10 + 10,它应该返回一个结果20,那么这么过程是怎么实现的呢?

  • 它首先会把10转换成二进制数字 0000 1010
  • 让两个二进制数字相加,得到结果 0001 0100,再转化成10进制就等于 20. 
  •  

       

从上面的计算过程可以看的出来,当前我们创造的这台计算机进行加法运算是完全没问题的.加法的问题是解决了,接下来又有一个问题.在我们常用的十进制中数字都分正和负比如 +10 和 -10,那在二进制中怎么表示一个数字是正数还是负数呢?

在10进制中我们用 + 表示正,用 - 表示负数.同理在二进制中可以 0 代表 + , 1 代表 - .比如一个8位的二进制数字 0000 0001.最高位也就是第八位作为符号位,为0就表示正数,为1就表示负数.所以 0000 0001 相当于十进制的 +1.而 1000 0001表示十进制的-1.按照此方法定义的二进制数据称为原码.

我们现在已经创建了一个叫做原码的编码方式.用原码表示的二进制数据进行加法运算是没问题的,并且它还能表示正负数.既然它能表示负数,那我们来测试一下减法运算.

         

  • 0000 0001等于十进制 +1,1000 0001 等于10进制 -1。1 + (-1) 就相当于 1 - 1

  • 1 - 1的结果等于0,但是二进制算出来的结果为1000 0010,等于10进制的 -2.计算结果明显不对.

  • 这原码的弊端就显现了,它只能做加法不能做减法.那要它有何用.所以接下里我们要寻找一种新的编码方式让二进制数字既能做加法也要能做减法.

 

补码的推导

现在需要重新寻找一种编码方式使二进制能做减法.我们可以从数学最本质的运算过程入手看能否寻找到规律.例如十进制运算 100 - 10 ,可以用以下的方式来进行运算:

100 -10 = 

100 -10 + 100 -100 = 

100 - 10 + 99 + 1 - 100 = 

100 + (99 - 10) + 1 - 100
  • 100 - 10 就相当于 100 + (-10), -10 转换成了 (99 - 10) + 1,这样做有什么好处呢?10是两位数,而99是两位数中最大的数,如果两个数的位数相同并且被减数是最大数时那么这个减法不会发生借位.减法之所以麻烦是因为小数减大数时需要向前一借位.而如今转换成了这种形式就避免了借位.
  • 做减法避免借位的关键在于看减数有几位并在后面加上一个再减去一个比减数的位数还大一位的数字.比如减数是10,那么在后面就加100再减100.如果减数是100,就在后面加1000再减1000.
  • 100可以转换成 99 + 1,如此 -10 就转换成为了 (99 -10) + 1
  • 从上述的推导过程可以得到一些启示.如果一个数是正数比如100,那么它不做任何处理还是100.但如果一个数是负数比如-10,它就可以转化成为这个数所在位数中的最大数减去这个数再加1.
  • 最大位的数减去一个数例如 99 -10 不就是10的反码吗?而反码再加 1 不就是补码吗.为了进一步阐述清楚,我们把上述计算过程换算成二进制来看.
100 - 10 =

01100100 - 00001010 = 

01100100 - 00001010 + 100000000 - 100000000 = 

01100100 - 00001010 + 11111111 + 1 - 100000000 = 

01100100 + (11111111 - 00001010) + 1 - 100000000
  • 我们以前在课本上经常看到的这样的结论:二进制正数的反码和补码都等于这个正数本身.二进制负数的反码等于符号位不变,其他位按位取反后的结果.二进制负数的补码等于该负数的反码加1.
  • 例如 1000 1101,它的反码可以这样转化:最高位符号位1不变,其他位按位取反 1变成0,0变成1,结果等于 1111 0010.比如一个三位无符号数001它按位取反的结果为110.那用 111 - 001 是不是就等于110.那换一句话说最大位的数减去这个数就是它的反码.
  • 我们再回头看上面的计算过程 (11111111 - 00001010) 就是 -00001010 的反码,再加个 1 就变成了补码.

再往下继续计算

01100100 + (11111111 - 00001010) + 1 - 100000000 = 

01100100 + 11110110 - 100000000 = 

  • 01100100 + 11110110 = 1 01011010 - 100000000 = 01011010,换成成十进制等于90,计算结果正确.
  • 上面 01100100 + 11110110 计算的结果产生了一个进位把后面的1 0000 0000给抵消掉了.0110 0100是十进制100的补码,1111 0110是十进制-10的补码.如果一正一负的补码相加,它如果产生了进位,最高位就等于0,它就是一个正数,正数的原码 = 反码 = 补码.如上两个补码相加得到的结果为1 0101 1010,产生了一个进位1(假设运算器最多存储8位)溢出了,抛弃掉结果就变成了01011010.它是一个正数,所以它的原码等于补码,通过原码就可以计算出十进制的大小.
  • 设想一下特殊的情况,两个补码相加没有产生进位,那么这两个数相加得到的数据假设为x,x最高位一定是1.后面可以继续换算. x - 1 0000 0000 =  x - 1111 1111 - 1 = -(1111 1111 - x + 1).x肯定是一个负数,所以符号位提出来,对x求反码再加1就得到了最终的值.从这里看的出来,符号位保持不变,对补码求补码就是它的原码.

 

总结

1.通过在数学世界的不懈探索,我们终于找到了一种编码方式(也就是补码)来实现减法运算.现在给计算机输入两个十进制的数据,比如100 -10,希望计算机最后能返回90给我.那么首先它会分别算出100的补码和-10的补码,让它们两个相加,得到的和还是补码,再对这个补码求补就能得到该数据的原码,通过原码也就能计算出对应的十进制的值了,此时这个值就可以返回给用户了.

2.如何求一个二进制数据的补码?

如果二进制数据(原码形式)的最高位为0,那么该条数据是一个正数.补码 = 反码 = 这条数据本身.

如果二进制数据(原码形式)的最高位为1,那么该条数据是一个负数.最高位符号位不变,其他位按位取反,再加1就得到了补码.

 3.补码的意义

对于人类而言原码是友好的,我们可以将十进制数据轻松的转化成二进制原码,也能通过原码计算出对应的十进制数据.原码虽然好,但是它做不了减法运算,这就是补码诞生的原因.对于计算机而言,原码和反码都没多大意义,它们都是为了迎接补码诞生的序章.计算机底层的二进制数据都是以补码的形式来存储,可以更加方便的进行算术运算.到此我们也明白了,最终任何外界的输入数据在计算机里都转换成了补码进行运算和存储,补码才是二进制数据的表示形式.

 

 

 

猜你喜欢

转载自blog.csdn.net/brokenkay/article/details/107381271