原码和补码
前面讲述了十进制整数和小数在计算机里面的存储,但是计算机终究是用来做计算的机器,归根到底它的作用还是用来计算,这一小节就来讲解一下存储在计算机中的二进制数是如何来运算的。首先我们来一起看一下二进制加法。
二进制加法的计算过程和十进制加法一样,在演算的时候可以列出竖式进行计算。如一个加法程序,用户输入两个十进制数85和78进行加法运算,首先计算机收到这两个数之后,将其存储起来,在运算时再将它们放入寄存器中,此时的它们存储时就已经是二进制了,(85)d=(01010101)b,(78)d=(01001110)b。计算式如下:
(10100011)b转成十进制就是(163)d。
这里告诉大家一个秘密,早期的x86 CPU内部是只能做加法,不管是加法还是减法,都是用加法器来计算的。加减法机制到目前的CPU上面还是如此,即减法和加法,CPU最终都是做的加法运算,而乘法和除法运算现在的CPU则和以前的有很大区别,后面会讲述。
其实计算机在存储变量的时候,计算机都是以补码方式存放的,这里引入了一个新概念叫做补码。在讲补码之前先阐述两个其他概念,原码和反码。
原码,其实就是二进制码本身,例如,一个十进制的数50,对其进制转换,转换成二进制是00110010,这个00110010就是其原码;
反码,就是将原码本身的每一位二进制数值取反,这是一种逻辑运算,简单来说就是把二进制的1转换成0,0转换成1,比如原码是00110010的二进制,其反码就是11001101。
上面的原码和补码都只是抛开符号来说的,但是计算机里面的数值都是以二进制存放的,计算机里面的数值分为有符号数,无符号数,浮点数这三大类。对于负数来说,计算机本身就不知道它到底是一个正数还是一个负数,因此需要对计算机存储器里面的数值进行定义,一般地,以一个二进制的最高比特作为符号位,当符号位为1时,表示这个数值是个负数,符号为0时,表示这个数值是一个正数。总而言之,不管是32位变量,64位变量还是8位变量,它们的最高位都是表示符号。
而补码的求解原则有两条:
(1) 正数的补码就是其原码;
(2) 负数的补码求法是:符号位保持不变,其他位取其反码再加1。
比如一个数值53,先求其二进制数(00110101)b,按照正数的补码就是其原码的原则,它的补码就是(00110101)b。
再比如一个数值-53,先求其二进制数(-110101)b,假设这个数值是以8位二进制存储的,那就将其符号位变成最高位,由于是负数,就可以得到它的二进制为(10110101)b,符号位保持不变,其他位取反(11001010)b,再加1,就可以得到其补码(11001011)b。
上述就是一个二进制数存储在计算机里面求补码的方式。
好了,让我们再来思考一个问题,已知所有的内存地址都是按照一个字节(8位二进制)来存储的,假设一个变量是1个字节,那么它存储无符号数值的时候,最多可以存储2^8=256种数值,存储数值的范围即从(00000000)b~(11111111)b,即0~255。而在存储一个有符号数值的时候,由于最高位要用来存储符号,而它存储的数值可能还是256个,只不过不同的是其数值范围变成了(-128~+127)。同样的,一个十六位存储宽度的数值,如果存储无符号数的时候,数值范围为0~65535,但是如果存储无符号数值的时候,其数值范围缩小一半,为-32768~+32767(勘误,感谢网友ID为“楔子”的朋友指出)。以上就是计算机存储数值的机制,其实对于计算机来说,它是不区分无符号数和有符号数的,它处理的都是二进制数,只不过在显示或者软件编程的时候需要区分。比如在计算机编程的时候,定义了一个八位二进制数,那么它在编译软件处理的时候就将其直接转成补码的形式存放起来。
理解了以上问题,我们再来看计算机如何做减法。其实初中数学就告诉我们,减法运算其实是加负数的运算,比如十进制数56-3,把它写成加法形式就是56+(-3),我们先假设这两个变量都是以8位二进制数,即一个字节的大小来存储的。存储到计算机里面首先需要将其都转为补码的形式,56由于是正数,因此其补码就是原码,为(00111000)b,-3由于是负数,因此其补码的求解方式就是符号位保持不变,其余位取反加一,为(11111101)b,接着让我们直接将其相加,看看会有什么结果,可以得到其结果为(100110101)b,得到的结果是一个九位的二进制数,由于我们之前假设了这个存储器是八位的,因此第9位属于溢出位,先不管它,取其低八位二进制(00110101)b,这个值是一个补码,接下来需要将它转换成原码。补码转原码的方式和原码转补码的原则一样,即最高位为符号位,正数的补码就是其原码,负数的补码求法是:符号位保持不变,其他位取其反码再加1。回头来看这个结果(00110101)b,它是个正数,因此原码就是它本身,为(35)H,转换成十进制就是53,因此56-3就是53。
为了给大家再详细地来解释一下这个问题,我们提前来用C语言写一个测试程序。代码如图1-6-1所示。
图1-6-1 示例程序
运行结果如图1-6-2所示。
图1-6-2 运行结果
上面的4294967295就是十六进制里面的(FFFFFFFF)H,这个数转成原码,其实就是-1。上例中我们可以得出一个结论,一个数的正负性,和它内部存储器上的内容无关,只和编译器想要它变成正数还是变成负数有关。
++++++++好书推荐++++++++
推荐理由:微机原理40多年的发展,到现在还在沿用X86架构,这本书中的内容非常详细,深入浅出
适应人群:
1、软件开发人员的理论加深;
2、计算机从业人员;
3、爱学习的童鞋。
推荐指数: