闲扯原码,补码和反码

始发于goal00001111的专栏;允许自由转载,但必须注明作者和出处

 

人类习惯使用十进制数进行数值计算,而计算机则采用二进制,所以为了让计算机帮助人类计算,首先要把十进制数转换为二进制数。本文以最简单的8位定点整数为例,分析了计算机存储和计算数值的方法。

地球人都知道,整数有正负之分,但计算机却只认得“0”“1”,不知道符号“+”和“-”,所以有必要用“0”“1”来表示“+”“-”。人们规定用“0”表示“+”,用“1”表示“-”。

       这样,我们就可以表示出计算机能识别的整数了,我们把符号数值化后的二进制数称为机器数,相对应的,符号没有数值化(即仍用“+”“-”号表示)的二进制数称为真值。计算机只能处理机器数,不认识真值,真值是给人类看的。

       机器数有三种编码形式,分别称为:原码,补码和反码。为什么要搞得这么复杂,那些计算机科学家真的是吃饱了没事干吗?且听我慢慢道来:

       其实篇头已经介绍了机器码的一种形式——原码,它的特点是有效数值部分照抄真值,符号“+”“-”分别用“0”“1”表示。

例如,十进制数+6,它的真值是+000 0110(注意:8位二进制数最高位是符号位,所以其真值只有7位),对应的原码就是0000 0110。

又如,十进制数-6,它的真值是-000 0110,对应的原码就是1000 0110。

原码表示法比较直观,它的数值部分就是该数的绝对值,而且与真值的转换十分方便。但是它的加减法运算较复杂,当两数相加时,机器要首先判断两数的符号是否相同,如果相同则两数相加,若符号不同,则两数相减。在做减法前,还要判断两数绝对值的大小,然后用大数减去小数,最后再确定差的符号,换言之,用这样一种直接的形式进行加运算时,负数的符号位不能与其数值部分一道参加运算,而必须利用单独的线路确定和的符号位。要实现这些操作,电路就很复杂,这显然是不经济实用的。为了减少设备,解决机器内负数的符号位参加运算的问题,总是将减法运算变成加法运算,也就引进了反码和补码这两种机器数。

那如何将减法运算转化为加法运算呢?

首先引入 “模”的概念,“模”是指一个计量系统的计数范围。以我们每天用来算时间的时钟为例,时钟的计量范围是0~11,所以它的模就等于12。计算机也可以看成一个计量机器,它也有一个计量范围,即存在一个“模”。 机器字长为n位的计算机的计量范围是0~2^n-1,模=2^n。

  “模”实质上是计量器产生“溢出”的量,它的值在计量器上表示不出来,计量器上只能表示出模的余数。例如,虽然时钟的模=12,但是在时钟的指针并不能真正指向“12点”,“12点”的位置和“0点”是重合的!用C语言表示就是12%12 == 0。

任何有模的计量器,均可化减法为加法运算。这是为什么呢?

仍然以时钟为例,假设当前时针指向10点,而准确时间是6点,调整时间可有以下两种拨法:

一种是倒拨4小时,即:10-4=6

另一种是顺拨8小时:10+8=12+6=6

在以12为模的系统中,加8和减4效果是一样的,因此凡是减4运算,都可以用加8来代替。

对“模”12而言,8和4互为补数。插一句,所谓“补数”,实际上是模拟了数学中“补角”的概念,如果两个角的度数之和为180度,我们就称这两个角互为补角。同样的,如果在某个计量系统中,两个数之和刚好等于模,则它们互为“补数”,例如,在以12为模的系统中,11和1,10和2,9和3,7和5,6和6都互为补数。

对于计算机,其概念和方法完全一样。机器字长为n位的计算机,设n=8, 所能表示的最大数是11111111,若再加1称为100000000(9位),但因只有8位,最高位1自然丢失,又回了00000000,所以8位二进制系统的模为2^8。 在这样的系统中减法问题可以化成加法问题,只需把减数用相应的补数表示就行了。

把补数用到计算机对数据的处理上,就是补码。补码也是一种机器码,它克服了原码的一些缺陷,一方面使符号位能与有效值部分一起参加运算,从而简化运算规则;另一方面使减法运算转换为加法运算,进一步简化计算机中运算器的线路设计。现代的计算机都是用补码的形式来存储数据和进行算术运算的。

那补码是如何编码的,即我们如何将一个整数的真值转换为一个8位补码呢?

回到最初的例子,十进制数+6。我们已经知道了它真值是+000 0110,原码是0000 0110。并且用自然语言介绍了如何实现真值和原码的转换。但是,一个众所周知的事实是:“自然语言”不如“数学语言”严谨!我们希望能够用数学表达式来表示真值和原码的关系,这就是:

设机器字长为N位,真值为X,则:

[X]原  = X,         0 <= X < 2^(n-1)

[X]原  = 2^(n-1) - X, -2^(n-1) < X <= 0   

如何来理解这个公式呢?

仍以十进制数+6和-6为例:

[+6]原  = 6,把6转换为8位二进制数,就得到原码0000 0110。(本文的最后将会提供一个把十进制数转换为机器码的C++算法实现)。

[-6]原  = 2^(8-1) – (-6) = 256 + 6 = 262,,把262转换为8位二进制数,就得到原码1000 0110。即最高位本来是0,加了一个2^(8-1)后,最高位就变成1了。

同样我们给出补码的数学表达式:

[X]补  = X,         0 <= X < 2^(n-1)

[X]补  = 2^n + X,   -2^(n-1) <= X < 0

和原码一样,正数的补码就等于真值,那如何理解负数的补码呢?

例如,[-6]补  = 2^8 + (-6) = 512 – 6 = 506。

且慢,这个506怎么这么熟悉!它不正是以2^8为模的6的“补数”吗?原来负数的补码就等于它的绝对值的补数啊!

把506转换为8位二进制数,就得到-6的补码1111 1010。

得到某个数的补码后,我们就可以把减法运算转化为加法运算了。

补码加法的运算法则为:[X +Y]补 = [X] 补 + [Y] 补

例1:X =+011 0011,Y=+010 1001,求[X+Y] 补

解:[X +Y]补 = [X] 补 + [Y] 补 = 0011 0011 + 0010 1001 = 0101 1100

例2:X =+011 0011,Y=-010 1001,求[X+Y] 补

解:[X +Y]补 = [X] 补 + [Y] 补 = 0011 0011 + 1101 0111 = 0000 1010 (进位溢出)

注:因为计算机中运算器的位长是固定的(本例中只有8位),上述运算中产生的最高位进位将丢掉,所以结果不是1 0000 1010,而是0000 1010。

补码减法公式:[X - Y]补 = [X] 补 -  [Y] 补= [X] 补 + [-Y] 补

其中:[-Y]补称为负补,求负补的办法是:对补码的每一位(包括符合位)求反,且未位加1。

例3:X =+011 0011,Y=+010 1001,求[X-Y] 补

解:[X - Y]补 =  [X] 补 + [-Y] 补 = 0011 0011 + 1101 0111 = 0000 1010 (进位溢出)

例2:X =+011 0011,Y=-010 1001,求[X-Y] 补

解:[X - Y]补 =  [X] 补 + [-Y] 补 = 0011 0011 + 0010 1001 = 0101 1100

根据补码加减运算得到的结果仍然是补码,若要将补码转换成原码,只要对其再求一次补码就行了。

再来说说反码。当初引入反码是为了解决原码运算所遇到的困难,但由于反码自身也存在一定的缺陷,加之补码在机器运算中的优越表现,完全掩盖了反码的光芒,以至于现在人们之所以提到反码,只是因为在用笔算将真值转换为补码的时候,可以快一些——先将原码转换为反码,然后反码加1,就得到了补码——但是对于计算机来说,反码这个中介完全是没有必要的。

反码和原码的关系很紧密,反码表示法规定:正数的反码与其原码相同;负数的反码是对其原码逐位取反,但符号位除外。

同样我们给出反码的数学表达式:

[X]反  = X,         0 <= X < 2^(n-1)

[X]反  = 2^n – 1 + X,   -2^(n-1) < X <= 0

 从数学表达式中我们可以发现,整数的三种机器码都是相同的,而负数的则不同。负数的反码是对原码按位求反(符合位除外),而补码则等于反码加1。这样有了反码这个中介,我们即使是用笔算也能够很快地将原码转换为补码了。例如:

[+6]反  = 6, [-6]反  = 2^(8-1) – 1 + (-6) = 255 + 6 = 261,,把261转换为8位二进制数,就得到反码1111 1001。

由于-6的原码为1000 0110,我们稍作观察,就可找到反码和原码的关系。

再将反码加1,就得到-6的补码1111 1010。

现在明白了吧?

 

附录:把十进制数转换为机器码的C++程序代码

 

#include <iostream>

using namespace std;

 

const int MAX = 32;

 

void Binary(char b[], int x); //将x转换为二进制数

void TrueForm(char b[], int x); //获取原码

void RadixMinus(char b[], int x); //获取反码

void Complement(char b[], int x); //获取补码

void TruthValue(char b[], int x);//获取真值

 

int main()

{

      int x = 1;

      char b[MAX+1]={0};

     

      cout << "十进制数:" << x << endl;

      TruthValue(b, x);//获取真值

      cout << "真值:" << b << endl;

     

      TrueForm(b, x); //获取原码

      cout << "原码:" << b << endl;

     

      RadixMinus(b, x);//获取反码 

      cout << "反码:" << b << endl;

     

      Complement(b, x);//获取补码

    cout << "补码:" << b << endl;

   

    cout << "十进制数:" << -x << endl;

      TruthValue(b, -x);//获取真值

      cout << "真值:" << b << endl;

     

      TrueForm(b, -x); //获取原码

      cout << "原码:" << b << endl;

     

      RadixMinus(b, -x);//获取反码 

      cout << "反码:" << b << endl;

     

      Complement(b, -x);//获取补码

    cout << "补码:" << b << endl;

     

    system("pause");

    return 0;

}

 

void Binary(char b[], int x)//将x转换为二进制数

{

    for (int i=MAX-1; i>=0; i--)

    {

           b[i] = (x & 1) + '0';

           x >>= 1;

      }

      b[MAX] = '\0';

}

 

void TrueForm(char b[], int x) //获取原码:根据数学表达式求得

{

    if (x >= 0)

          Binary(b, x);

      else

          Binary(b, (1<<(MAX-1)) - x);

}

 

void RadixMinus(char b[], int x) //获取反码:正数的反码=补码;负数的反码=补码-1

{

    if (x >= 0)

          Binary(b, x);

      else

          Binary(b, x - 1);

}

 

void Complement(char b[], int x) //获取补:数据在计算机中以补码形式存储,直接转换即可

{

    Binary(b, x);

}

 

void TruthValue(char b[], int x)//获取真值:根据原码获得真值

{

    TrueForm(b, x);

      b[0] = (b[0] == '0') ? '+' : '-';  

}

 

参考文献:

(1)Boater的博客:《反码和补码技术是怎样被提出的?》

http://blog.tianya.cn/blogger/post_show.asp?BlogID=227218&PostID=7046448

(2)北半球的孤独发帖:《关于机器数的几点注记》

http://forum.noi.cn/thread-29319-1-1.html

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/goal00001111/archive/2010/04/15/5490612.aspx

Posted on 2010-04-16 08:41 梦想飞扬 阅读(1323) 评论(2)  编辑 收藏 引用

Feedback

# re: 闲扯原码,补码和反码  回复  更多评论   

2010-09-20 11:35 by wangqiulin
能否解释下那程序的代码比如那十进制转换成二进制的函数
我不是很懂
嘻嘻
谢谢

# re: 闲扯原码,补码和反码  回复  更多评论   

2012-03-27 03:56 by 小小
X-QQ-SSF: 0001000000000020000000000000000
X-HAS-ATTACH: no
X-QQ-BUSINESS-ORIGIN: 2
X-Originating-IP: 116.228.158.117
X-QQ-STYLE:
X-QQ-mid: webmail48t1332596745t453944
From: "=?gbk?B?ICAgICAgICCxobrJoaI=?=" <291402550@qq.com>
To: "=?gbk?B?saa2+SAgbG92ZQ==?=" <807074045@qq.com>
Subject: =?gbk?B?u9i4tKO6ztLP1tTayKXE47/VvOS55Lnk?=
Mime-Version: 1.0
Content-Type: multipart/alternative;
boundary="----=_NextPart_4F6DD009_080C9160_4A035903"
Content-Transfer-Encoding: 8Bit
Date: Sat, 24 Mar 2012 21:45:45 +0800
X-Priority: 3
Message-ID: <tencent_760FA40C52C727CB5CFD5D1B@qq.com>
X-QQ-MIME: TCMime 1.0 by Tencent
X-Mailer: QQMail 2.x
X-QQ-Mailer: QQMail 2.x
X-QQ-ReplyHash: 3140746782

This is a multi-part message in MIME format.

------=_NextPart_4F6DD009_080C9160_4A035903
Content-Type: text/plain;
charset="gbk"
Content-Transfer-Encoding: base64

udzE48qyw7TKwqGj1NvBqc/W1NrS0b6tt9bK1sHLo6yw3c3QsfC24Lncz9DKwqGjvs3L48Tj
us3LrbTytee7sLa8w7vT0NPDo6yx8NPDxOPEx9XQzf7Qss7SDQogIA0KICAtLS0tLS0tLS0t
LS0tLS0tLS0g1K3KvNPKvP4gLS0tLS0tLS0tLS0tLS0tLS0tDQogILeivP7IyzogIrGmtvkg
bG92ZSAiPDgwNzA3NDA0NUBxcS5jb20+Ow0KILeiy83KsbzkOiAyMDEyxOoz1MIyNMjVKNDH
xtrB+Skgze3Jzzk6NDMNCiDK1bz+yMs6ICIyOTE0MDI1NTAiPDI5MTQwMjU1MEBxcS5jb20+
OyANCiANCiDW98ziOiDO0s/W1NrIpcTjv9W85LnkueQ=

------=_NextPart_4F6DD009_080C9160_4A035903
Content-Type: text/html;
charset="gbk"
Content-Transfer-Encoding: base64

PERJVj4mbmJzcDs8L0RJVj4NCjxESVY+PGluY2x1ZGV0YWlsPg0KPERJVj4mbmJzcDs8L0RJ
Vj4NCjxESVY+udzE48qyw7TKwqGj1NvBqc/W1NrS0b6tt9bK1sHLo6yw3c3QsfC24Lncz9DK
wqGjvs3L48Tjus3LrbTytee7sLa8w7vT0NPDo6yx8NPDxOPEx9XQzf7Qss7SPC9ESVY+DQo8
RElWPiZuYnNwOzwvRElWPg0KPERJViBzdHlsZT0iQ09MT1I6ICMwMDAiPg0KPERJViBzdHls
ZT0iUEFERElORy1SSUdIVDogMHB4OyBQQURESU5HLUxFRlQ6IDBweDsgRk9OVC1TSVpFOiAx
MnB4OyBQQURESU5HLUJPVFRPTTogMnB4OyBQQURESU5HLVRPUDogMnB4OyBGT05ULUZBTUlM
WTogQXJpYWwgTmFycm93Ij4tLS0tLS0tLS0tLS0tLS0tLS0mbmJzcDvUrcq808q8/iZuYnNw
Oy0tLS0tLS0tLS0tLS0tLS0tLTwvRElWPg0KPERJViBzdHlsZT0iUEFERElORy1SSUdIVDog
OHB4OyBQQURESU5HLUxFRlQ6IDhweDsgRk9OVC1TSVpFOiAxMnB4OyBCQUNLR1JPVU5EOiAj
ZWZlZmVmOyBQQURESU5HLUJPVFRPTTogOHB4OyBQQURESU5HLVRPUDogOHB4Ij4NCjxESVYg
aWQ9bWVudV9zZW5kZXI+PEI+t6K8/sjLOjwvQj4mbmJzcDsisaa2+SBsb3ZlICImbHQ7ODA3
MDc0MDQ1QHFxLmNvbSZndDs7PC9ESVY+DQo8RElWPjxCPreiy83KsbzkOjwvQj4mbmJzcDsy
MDEyxOoz1MIyNMjVKNDHxtrB+Skgze3Jzzk6NDM8L0RJVj4NCjxESVY+PEI+ytW8/sjLOjwv
Qj4mbmJzcDsiMjkxNDAyNTUwIiZsdDsyOTE0MDI1NTBAcXEuY29tJmd0OzsgPFdCUj48L0RJ
Vj4NCjxESVY+PC9ESVY+DQo8RElWPjxCPtb3zOI6PC9CPiZuYnNwO87Sz9bU2silxOO/1bzk
ueS55DwvRElWPjwvRElWPjwvRElWPjwvaW5jbHVkZXRhaWw+PC9ESVY+

------=_NextPart_4F6DD009_080C9160_4A035903--

只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理