用补码表示整数及加减运算

来源:互联网 发布:焊楼梯尺寸怎么算法 编辑:程序博客网 时间:2024/04/30 05:46

 本次我组主要是想通过运用补码来表示整数,我主要的工作是将十进制整数转化成补码及其加减运算和溢出的讨论。


一、原理


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

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

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


(一)原码


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

例如:

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

-6D,它的真值是-000 0110,对应的原码就是1000 0110。

原码表示法比较直观,它的数值部分就是该数的绝对值,而且与真值的转换十分方便。


(二)补码


机器数的补码可由原码得到。如果机器数是正数,则该机器数的补码与原码一样;如果机器数是负数,则该机器数的补码是对它的原码(除符号位外)各位取反,并在未位加1而得到的。设有一数X,则X的补码表示记作[X]

那补码是如何编码的,对于十进制+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转换为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

和原码一样,正数的补码就等于真值。负数的补码则该机器数的补码是对它的原码(除符号位外)各位取反,并在未位加1而得到的。

(三)反码

机器数的反码可由原码得到。如果机器数是正数,则该机器数的反码与原码一样;如果机器数是负数,则该机器数的反码是对它的原码(符号位除外)各位取反而得到的。设有一数X,则X的反码表示记作[X]。反码和原码的关系很紧密,反码表示法规定:正数的反码与其原码相同;负数的反码是对其原码逐位取反,但符号位除外。

(四)补码的加法运算规则


[X+Y]补= [X]补+[Y]补该式表明,当有符号的两个数采用补码形式表示时,进行加法运算可以把符号位和数值位一起进行运算(若符号位有进位,则丢掉),结果为两数之和的补码形式。

例如用补码进行下列运算:(+33)补+(+15)补;(+33)补+(-15)补。

计算这样一个补码数时,首先要记得把相应的补码数写成二进制数(有符号位)。如图所示的就是以上两个补码加法运算式。最终的结果分别是[+48]补和[+18] 补。

00100001 【+33】 00100001 【+33】

00001111 【+15】 11110001 【-15】

---------------------------------------------------------------------------

00110000 【+48】 100010010 【+18】

图 两个补码加法运算示例


(五)补码的减法运算规则


[X-Y]补=[X]补+[-Y]补

该式表明,求[X-Y]补可以用[X]补与[-Y]补相加来实现。

[-Y]补是对减数进行求负操作。一般称已知[Y]补求得[-Y]补的过程叫变补或求负。已知[+Y]补求[-Y]补的规则是全部位(含符号位)按位取反后再加1。

例如:已知[+15]补=00001111B,则[-15]补=11110000B+1=11110001B
或:0- [+15]补=0–10001111B=11110001B

现在例举补码减法运算示例。仍假设X=+33,Y=+15,现要求[X-Y]补。

先求得[X]补=00100001B,[Y]补=00001111B,根据以上介绍的补码负操作规则,可以得到[-Y]补=11110001B。然后再用[X]补+[-Y]补公式即可得到最终的[X–Y]补。运算过程如图所示。如果是X= -33,Y= -15,如果仍要求[X-Y]补,则同样需要求[-Y]补,也即[-(-15)]补,实际上是要求[+15]的补码。因为已知了[-15]补=11110001B,根据前面介绍的补码负操作规则,可以很快得出[+15]补=00001111B。这样[X-Y]补就等于[-33]补+[+15]补,运算过程如图2-15右图所示。

001000001 【X】补 11011111 【-33】

+1111000 1 【-Y】补 + 00001111 【+15】

-----------------------------------------------------------------------------------------

100010010 【+18】 11101110 【-18】

图 两个补码减法运算示例


(六)溢出讨论


 下面的问题是如何检查加减运算中的溢出问题。通常有三种表述方式(说法):
  (1) 两个符号相同的补码数相加,如果和的符号与加数的符号相反,或两个符号相反的补码数相减,差的符号与减数的符号相同,都属于运算结果溢出。这种判别方法比较复杂,要区别加还是减两种不同运算情况,还要检查结果的符号与其中一个操作数的符号的同异,故很少使用;

  (2) 两个补码数相加减时,若最高数值位向符号位送的进位值与符号位送向更高位的进位值不相同,也是运算结果溢出。

  (3) 在采用双符号位(如定点小数的模4补码)运算时,若两个符号位的得值不同(01或10)则是溢出。01表明两个正数相加,结果大于机器所能表示的最大正数,称为"上溢";10表明两个负数相加,结果小于机器所能表示的最小负数,称为"下溢";双符号位的高位符号位,不管结果溢出否,均是运算结果正确的符号位,这个结论在乘法运算过程中是很有实际意义的。请注意,在采用双符号位的方案中,在寄存器和内存储器存储数据时,只需存一位符号,双符号位仅用在加法器线路部分。


二、思路

本次的思路主要是根据数值间的转化设计,运用原理的思想将十进制转化成二进制,进而求出原码和补码。

首先,定义一个函数void Binary(int n)将正整数转化成二进制(负数的比较特别,后面用另外的函数求得)其中的变量n是输入的十进制正整数。其转化方式主要是运用除2取余法!将结果保留在一维数组中,方便以后使用。其次定义了输出函数void Print(int a[])主要是输出最后求出的二进制、原码。由于补码的输出特别些,我目前想不到更好的方法就另加啦个输出补码的输出函数void Printbm(int a[]),其主要原因是将最高位置1(用了||运算符)。最后,就是再定义了一个求负数的二进制的函数void Nbinary(int a[])主要是对应负数的二进制的各位作啦相应变换。其一负数的二进制是其正数二进制的各位取反再加1,其二就是对进位的处理,从低到高一次扫描,发现2时就将其置1,再将其下一位加1,依次类推,则可以求出负整数的二进制码,再运用相应的运算求出其原码,补码。

程序的结尾就是运用main()函数调用函数输出结果。


三、流程图



四、程序源代码

#include "stdafx.h"#include "iostream"using namespace std;const int MAX=32;int a[MAX]={0};int c[MAX]={0};void Delete(int a[])//主要是清除运行时修改的,将其赋成¨{for(int i=0;i<MAX;i++)a[i]=0;}void Binary(int a[],int n)//正数的二进制转换{Delete(a);int b,i=0;while(n>0)//除取余法{b=n%2;n=(n-b)/2;a[i]=b;i++;}}void Print(int a[])//输出函数{for( int i=MAX-1;i>=0;i--){cout<<a[i];}}void Nbinary(int a[],int x)//负数的二进制转换函数{Delete(a);int b,i=0;x=-x;while(x>0)//除取余法{b=x%2;x=(x-b)/2;a[i]=b;i++;}for(int i=0;i<MAX;i++)//相应位取反{if(a[i]==0)a[i]=1;elsea[i]=0;}a[0]=a[0]+1;//取反后再在末位加一,转换成负数的二进制码for(int i=0;i<MAX;i++){if(a[MAX-1]==2)a[MAX-1]=0;elseif(a[i]==2)//处理相加后的进位?{a[i]=0;a[i+1]=a[i+1]+1;}elsebreak;}}void Printf(int x){if(x>=0){Binary(a,x);//正数的二进制和补1码一致cout<<"二进制:";Print(a);cout<<endl;cout<<" 补码:";Print(a);cout<<endl;}else{Nbinary(a,x);//负数y的补1码和二进制也一样cout<<"二进制:";Print(a);cout<<endl;cout<<" 补码:";Print(a);cout<<endl;}}void add(int a[],int b[])//补码加法{int d[MAX]={0};for(int i=0;i<MAX;i++){d[i]=d[i]+a[i]+b[i];if(d[i]==2)//处理相加后的进位{d[i]=0;d[i+1]=d[i+1]+1;}else if(d[i]==3){d[i]=1;d[i+1]=d[i+1]+1;}}for(int i=MAX-1;i>=0;i--){cout<<d[i];}cout<<endl;}void sub(int a[],int b[])//补码减法{for(int i=0;i<MAX;i++)//相应位取反{if(b[i]==0)b[i]=1;elseb[i]=0;}b[0]=b[0]+1;//取反后再在末位加一,转换成负数的[Y]补1for(int i=0;i<MAX;i++){if(b[MAX-1]==2)b[MAX-1]=0;elseif(b[i]==2)//处理相加后的进位{b[i]=0;b[i+1]=b[i+1]+1;}elsebreak;}add(a,b);}int _tmain(int argc, _TCHAR* argv[]){int x,y,z;cout<<" Please input the first integer :";cin>>x;Printf(x);//输出第一个数for(int i=0;i<MAX;i++){c[i]=a[i];}cout<<" Please input the second integer :";cin>>y;Printf(y);//输出第二个数cout<<endl;cout<<"The Result of the addition (a+b) is :";add(c,a);cout<<"TheResult of the subtraction (a-b) is :";sub(c,a);return 0;system("pause");}