计算机中带符号的整数为何采用二进制的补码进行存储?

本文介绍了计算机内部采用补码表示有符号整数的原因,探讨了原码、反码和补码的概念,并详细解释了为何选择补码,包括统一+0和-0的表示、简化运算规则等。

**本文转载自:博客园—- 海子的博客
http://www.cnblogs.com/dolphin0520/archive/2012/10/09/2711768.html**

我们都知道在计算机内部数据的存储和运算都采用二进制,是因为计算机是由很多晶体管组成的,而晶体管只有2种状态,恰好可以用二进制的0和1表示,并且采用二进制可以使得计算机内部的运算规则简单,稳定性高。在计算机中存在实数和整数,而整数又分为无符号整数和有符号整数,无符号的整数表示很简单,直接采用其二进制形式表示即可,而对于有符号数的表示却成了问题,如何表示正负?如何去处理正负号?下面来具体说下其中的原因,在这之前先了解一下原码、反码和补码这几个概念。

1.原码、反码和补码的概念

  在了解原码、反码和补码之前先说一下有符号数和无符号数。用过C语言的都知道在C语言中用signed和unsigned来标识一个数是否是有符号还是无符号类型的。对于一个8bit的二进制来说,若当做无符号数处理,其能表示的整型值范围是0~255,但是这样表示数据就有个局限性,如果数据是负的该如何表示?因此就引入了有符号类型的概念,对于有符号类型,规定取最高位为符号位,若最高位为0,则为正数,否则为负数,这样一来对于8位二进制,示数值的就只有7位了,能够表示的非负数值范围变为0~127,负值范围为-127~-1,相当于可以理解为将无符号类型能够表示的128~255拿来去表示-127~-1了。事实上,在计算机内部存储中,计算机自己是无法去区分无符号还是有符号类型的,对于255和-1,在计算机内部存储的都是11111111。换个角度来说,如果事先知道内存中存储了这样一个8位二进制11111111,但是谁也不能肯定它具体表示什么数值,是-1还是255?这个是需要靠程序员自己去指定的,如果指定为无符号类型,则编译器则通过相应指令将其转换为数值255。事实上对于-x的二进制补码表示形式和(256-x)(256-x当做无符号类型处理)的二进制表示形式相同,从这里可以略微了解了补码的含义了。在教材中对于原码、反码以及补码一般是这么定义的:

  对于正数原码、反码以及补码是其本身。负数的原码是其本身,反码是对原码除符号位之外的各位取反,补码则是反码加1。

  因为(-x)的二进制补码形式和256-x的二进制表示形式相同,而255-x相当于对x的每一位取反,那么256-x就是255-x后加1。

  注意:1)原码、反码、补码的概念是针对有符号类型而言的。
     2)实数始终是有符号类型的(实数并不是采用补码形式存储的,具体可参考《浅谈C/C++的浮点数在内存中的存储方式》一文),整型数据包括无符号和有符号类型的。

2.采用补码表示带符号的整数的原因

  对于有符号类型的整数,有原码、反码和补码三种形式,最后选择了补码来表示,具体来说有下面几点原因。

  1)能够统一+0和-0的表示

  采用原码表示,+0的二进制表示形式为0 000 0000,而-0的二进制表示形式为1 000 0000;

  采用反码表示,+0的二进制表示形式为0 000 0000,而-0的二进制表示形式为1 111 1111;

  采用补码表示,+0的二进制表示形式为0 000 0000,而-0的二进制表示形式为1 111 1111+1=1 0000 0000,因为计算机会进行截断,只取低8位,所以-0的补码表示形式为0000 0000。

  从上面可以看出只有用补码表示,+0和-0的表示形式才一致。正因为如此,所以补码的表示范围比原码和反码表示的范围都要大,用补码能够表示的范围为-128~127,0~127分别用00000000~01111111来表示,而-127~-1则用10000001~11111111来表示,多出的10000000则用来表示-128。因此对于任何一个n位的二进制,假若表示带符号的整数,其表示范围为-2^(n-1)~2^(n-1)-1,且有MAX+1=MIN。看下面一段代码:

char ch=127;
ch++;

ch的值是多少?它的值是-128,读者可以上机验证一下。

  假如不采用补码来表示,那么计算机中需要对+0和-0区别对待,显然这个对于设计来说要增加难度,而且不符合运算规则。

  2)对于有符号整数的运算能够把符号位同数值位为一起处理

  由于将最高位作为符号位处理,不具有实际的数值意义,那么如何在进行运算时处理这个符号位?如果单独把符号位进行处理,显然又会增加电子器件的设计难度和CPU指令设计的难度,但是采用补码能够很好地解决这个问题。下面举例说明:
  比如-2+3=1

   如果采用原码表示(把符号位同数值位一起处理):
   1 000 0010+0 000 0011=1 000 0101=(-5)原,显然这个结果是错误的。

  
  如果采用反码表示:
  1 111 1101+0 000 0011=1 0000 0000=0 0000000=(+0)反,显然这个结果也是错误的。
  
  如果采用补码表示:
  1 111 1110+0 000 0011=1 0000 0001=0000 0001=(1)补,结果是正确的。
  从上面可以看出,当把符号位同数值位一起进行处理时,只有补码的运算才是正确的。如果不把符号位和数值位一起处理,会给CPU指令的设计带来很大的困难,如果把符号位单独考虑的话,CPU指令还要特意对最高位进行判断,这个对于计算机的最底层实现来说是很困难的。

  3)能够简化运算规则
  对于-2+3=1这个例子来说,可以看作是3-2=1,也即[3]+[-2]=1,从上面的运算过程可知采用补码运算相当于是
  [3]补+[-2]补=[1]补,也即可以把减法运算转换为加法运算。这样一来的好处是在设计电子器件时,只需要设计加法器即可,不需要单独再设计减法器。
  总的来说,采用补码主要有以上几点好处,从而使得计算机从硬件设计上更加简单以及简化CPU指令的设计。

测试代码:

#include<stdio.h>
int main(void)
{
    char ch=-1;
    char *p=(char *)&ch;
    unsigned char uch=*p;
    printf("%d\n",uch);   //输出结果为255
    return 0;
}
计算机组成原理中采用补码表示带符号整数,主要有以下原因: ### 统一加减法运算 使用补码可以将减法运算转换为加法运算,从而简化计算机的运算电路设计。在计算机中,只需要设计加法器,就可以完成加法和减法两种运算。例如,计算`5 - 3`,可以转换为`5 + (-3)`的补码加法运算。以4位二进制数为例,`5`的二进制表示是`0101`,`-3`的原码是`1011`,补码是`1101`,则`0101 + 1101 = 1 0010`,舍去最高位的进位后得到`0010`,即十进制的`2`。 ### 简化0的表示 在原码和反码表示中,0有两种表示形式,即`+0`和`-0`。例如,在8位二进制中,`+0`的原码是`0000 0000`,`-0`的原码是`1000 0000`;`+0`的反码是`0000 0000`,`-0`的反码是`1111 1111`。这会给计算机的设计和处理带来额外的复杂性。而补码表示中,0只有一种表示形式,8位二进制中0的补码就是`0000 0000`,避免了这种歧义,减少了电路设计的复杂度和出错的可能性。 ### 增加表示范围 与原码和反码相比,补码可以多表示一个负数。以8位二进制数为例,原码和反码能表示的范围是`-127 ~ +127`,而补码能表示的范围是`-128 ~ +127`。这是因为原码和反码中`0`有两种表示方式,浪费了一个编码,而补码中`0`只有一种表示方式,多出的一个编码可以用来表示一个额外的负数。`-128`的补码是`1000 0000`。 以下是Python代码示例,展示了补码的运算: ```python def to_complement(num, bits): if num < 0: num = (1 << bits) + num binary_str = bin(num & ((1 << bits) - 1))[2:] return binary_str.zfill(bits) # 示例:计算 5 - 3 a = 5 b = -3 bits = 4 a_complement = to_complement(a, bits) b_complement = to_complement(b, bits) result = int(a_complement, 2) + int(b_complement, 2) result_complement = to_complement(result, bits) print(f"{a} 的补码: {a_complement}") print(f"{b} 的补码: {b_complement}") print(f"结果的补码: {result_complement}") print(f"结果的十进制值: {int(result_complement, 2)}") ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值