天天看點

(實驗三)補碼的加減法

1. 實驗要求

​ 用C/C++程式設計實作兩個十進制整數(将其轉換成補碼)加、減運算結果,提示思想:模4補碼進行計算

2. 相關知識點

​ 機器字長全部假設為 8 位,隻讨論整數,後不再特殊說明

0. 符号位

​ 之前我們符号位都是用 1 位表示,模 4 補碼是用 2 位表示,對應關系如下:

符号位 表示
00 正數
01 上溢
10 下溢
11 負數

特别提醒:補碼運算符号位作為數的一部分參與運算

1. 補碼加法

​ X + Y = [X] 補 _補 補​ + [Y] 補 _補 補​

  1. 符号位參與運算
  2. 如果有多出來的第三個符号位直接丢棄
  3. 運算結果也是補碼
  4. 最終将運算結果轉回真值

2. 補碼減法

​ X - Y = [X] 補 _補 補​ + [-Y] 補 _補 補​

​ 規則同補碼加法

3. 實作思路

0. 準備

包括:

  1. 檢查輸入是否合法,即第一個輸入為"+“或”-",其後輸入為數字
  2. 确定輸入十進制的正負
  3. 将輸入的 string 轉換為 int 類型

1. 轉二進制

​ 由準備工作準備好的 int 類型的數字直接轉換為二進制,不過要注意兩點,一是當數字為 0 時,取不到值,這時我們自己手動添個 0 上去,二是當數字為負數時,結果就很迷…解決方法是用其他變量暫存數字的絕對值

2. 轉相反數的補碼

​ 和轉補碼類似,轉的時候想到它的相反數

3. 實作加法

​ 對于字元串來說,需要先把倆運算的字元串逆序,算出結果了再逆序一次,需要注意的地方就是注意保留進位和進位每次一起運算

3. 補碼轉真值

​ 生成的結果可能會産生進位,比如 8 位 + 8 位 = 9 位,這種情況下最高位要丢棄,然後判斷此時的符号位是 11 10 01 還是 00,10 和 01 的情況是溢出,11 為負,00 為正

​ 但是有特殊情況,比如 [11101010] 補 _補 補​ + [11010110] 補 _補 補​ = [111000000] 補 _補 補​,此時應該為溢出,需要在 11 單獨處理

4. 具體實作

#include<iostream>
#include<cmath>  //abs
#include<malloc.h>  //malloc
#include<algorithm>  // reverse
#include<string>   // string
#include<sstream>  // stringstream
#define PLUS 1    // 正
#define MINUS -1   // 負
#define SYMBOLNUM 2   // 符号位長度
#define WORD_LENGTH 8  // 機器字長
#define MIN (-(1<<(WORD_LENGTH-SYMBOLNUM)))   // 最小取值範圍
#define MAX ((1<<(WORD_LENGTH-SYMBOLNUM))-1)  // 最大取值範圍
using namespace std;
typedef struct Integer *Number;
struct Integer{        // 定義一個結構體,把該數的各種碼放進去
	string input;  // 輸入的帶符号十進制整數
	string binary;  // 二進制真值   X
	string yuan_code;   // 原碼
	string between_bu_code;    // 二進制真值的相反數的補碼  [-X]補
	string bu_code; // 補碼   [X]補
	string yi_code;    // 移碼
	int symbol;  // 符号位
	int decimal; // 十進制整數
};
/* 準備工作
1.檢查輸入是否合法
2.确定輸入十進制數的正負
3.将 string 類型轉換成 int 類型 
4.确定輸入十進制數的範圍
*/
void prepare(Number num){
	// 驗證輸入合法性,确定該十進制整數的正負
	for(int i=0;i<(num->input.size());i++){
		if(num->input[0]=='+' && !i){    // 如果第一個字元為 +
			num->symbol = PLUS;    // 記錄符号為 +
		}else if(num->input[0]=='-' && !i){  //如果第一個字元為 -
			num->symbol = MINUS;   // 記錄符号為 -
		}else if(!((num->input[i]>='0' || num->input[i]<='9') && i)){  // 如果不是第一個字元,且不在 0~9 之間
			cout<<"輸入不合法,請重新啟動!\n"<<endl;    
			exit(0);            // 提示并結束程式
		}
	}
	// 轉換輸入十進制整數的類型
	stringstream container;   // 轉換容器
	container<<num->input.substr(1);      // "吞"進輸入的十進制整數 
	container>>num->decimal;      // 再将容器中的數"吐"出來
	num->decimal *= num->symbol;  // 帶上符号
	
	//确定輸入十進制數的範圍
	if(num->decimal < MIN || MAX < num->decimal){  // 如果取值比最小值還小,或者比最大值還大,說明超出表示範圍了
		cout<<"超出表示範圍,請重新啟動!\n"<<endl;
		exit(0);     // 提示并結束程式
	}
}
// 轉二進制
void tranfer(Number num){
	int tmpDecimal = abs(num->decimal); 
	// 特殊情況 0 
	// 如果 decimal 為 0,初始化字元串為 0,否則初始化為空
	num->binary=(tmpDecimal==0?"0":""); 
	while(tmpDecimal){
		num->binary += tmpDecimal%2+'0';
		tmpDecimal /=2;
	}
	reverse(num->binary.begin(),num->binary.end());  // 逆轉字元串
}
// 轉換成原碼
void ToYuan_code(Number num){
	// 如果輸入的是MIN,隻有補碼能表示,原碼置為 ——
	if(num->decimal == MIN){
		num->yuan_code = "——";
		return;
	}
	// 确定原碼符号位
	if(PLUS == num->symbol)   // 如果為正,符号位為 00
		num->yuan_code += "00";
	else     // 否則符号位為 1
		num->yuan_code += "11";
	//确定原碼數值位
	num->yuan_code += num->binary;  // 原碼數值位和二進制相等
	//補充中間的 0
	while(num->yuan_code.size() < WORD_LENGTH)   
		num->yuan_code.insert(SYMBOLNUM,"0");   // 在符号位後面追加 0
}
// 轉換補碼
void ToBu_code(Number num){
	if(num->symbol == PLUS){    // 如果是正數,補碼 = 原碼
		num->bu_code = num->yuan_code;
		return;
	}

	// 如果輸入的是MIN,隻有補碼能表示
	if(num->decimal == MIN){
		num->bu_code += "11";
		while(num->bu_code.size() < WORD_LENGTH)
			num->bu_code.insert(1,"0");   // 在符号位後面追加 0
		return;
	}
	//确定數值位
	bool flag = true;
	for(int i=num->yuan_code.size()-1;i>=SYMBOLNUM;i--){ //從低到高位
		if(num->yuan_code[i]=='1'){   
			if(flag){   // 當第一次遇到 1 時,改變标記值,
				num->bu_code +="1";
				flag = false;
			}
			else
				num->bu_code +="0";
		}else{   // 0
			if(flag)
				num->bu_code +="0";
			else
				num->bu_code +="1";
		}
	}
	reverse(num->bu_code.begin(),num->bu_code.end());  // 逆轉數組
	
	//确定符号位
	if(num->decimal)   // 隻要不是剛好 -0
		num->bu_code.insert(0,"11");
	else
		num->bu_code.insert(0,"00");
}
// 轉換負的補碼
void ToBetweenBu_code(Number num){
	if(num->symbol == MINUS){    // 如果是負數,負的補碼為正
		num->between_bu_code ="00" + num->yuan_code.substr(SYMBOLNUM);
		return;
	}

	//确定數值位
	bool flag = true;
	for(int i=num->yuan_code.size()-1;i>=SYMBOLNUM;i--){ //從低到高位
		if(num->yuan_code[i]=='1'){   
			if(flag){   // 當第一次遇到 1 時,改變标記值,
				num->between_bu_code +="1";
				flag = false;
			}
			else
				num->between_bu_code +="0";
		}else{   // 0
			if(flag)
				num->between_bu_code +="0";
			else
				num->between_bu_code +="1";
		}
	}
	reverse(num->between_bu_code.begin(),num->between_bu_code.end());  // 逆轉數組
	
	//确定符号位
	if(num->decimal)   // 隻要不是剛好 -0
		num->between_bu_code.insert(0,"11");
	else
		num->between_bu_code.insert(0,"00");
}
// 補碼轉真值
void bu_code2true_value(string result){
	// 去掉多餘的位數(也許進位産生了 3 位符号位)
	reverse(result.begin(),result.end());  // 先逆序
	result = result.substr(0,8);   // 從 0 開始,截取 8 位 
	reverse(result.begin(),result.end());  // 再逆轉回去
	
	string true_value;  // 真值
	// 提取符号位
	string symbol = result.substr(0,2);

	// 判斷符号位
	if(symbol=="11"){  // 如果為 11,真值為負
		bool flag = true;
		for(int i=result.size()-1;i>=SYMBOLNUM;i--){ //從低到高位
			if(result[i]=='1'){   
				if(flag){   // 當第一次遇到 1 時,改變标記值,
					true_value +="1";
					flag = false;
				}
				else
					true_value +="0";
			}else{   // 0
				if(flag)
					true_value +="0";
				else
					true_value +="1";
			}
		}
		reverse(true_value.begin(),true_value.end());  // 逆轉數組
		 
		// 如果此時真值全為 0,真值又是負,隻可能是溢出到 -2^(機器字長-符号位長),根本存不下
		string tmp="";
		while(tmp.size() < WORD_LENGTH-SYMBOLNUM)
			tmp += "0";
		if(true_value == tmp){
			cout<<"溢出"<<endl;
			return;
		}
		// 否則其他情況下才可能是個正常負值
		true_value.insert(0,"-");  // 最前面插入負号
	}
	else if(symbol=="10" || symbol=="01"){  // 如果為 01 或者 10,則溢出
		cout<<"溢出"<<endl;
		return;
	}
	else if(symbol=="00")
		true_value = "+"+result.substr(2);
	// 把數值前面多餘的0去掉
	for(int i=1;i<true_value.size();i++)
		if(true_value[i]=='1'){  // 第一次遇到 1,切到前面的 0 
			true_value =true_value[0]+true_value.substr(i);
			break;
		}
	cout<<true_value<<endl;
}
// 實作加法,如果 on 為 ture,[x+y]補,否則 [x-y]補
void Add(string x,string y,bool on){
	string result = "";   // 暫存結果  result = [x]補 + [y]補
	
	// 逆序為求和做準備
	reverse(x.begin(),x.end());
	reverse(y.begin(),y.end());

	int flag = 0;   // 記錄進位
	for(int i=0;i<WORD_LENGTH;i++){
		if(x[i]-'0' + y[i]-'0' + flag >=2){  // 如果和大于等于 2,進位為 1
			result += (x[i]-'0' + y[i]-'0' + flag)%2 +'0';
			flag = 1;
		}else{
			result  += x[i]-'0' + y[i]-'0' + flag + '0';
			flag = 0;
		}
	}
	if(flag)
		result +="1";
	// 最後結果逆序
	reverse(x.begin(),x.end());
	reverse(y.begin(),y.end());
	reverse(result.begin(),result.end()); 

	cout<<"  "<<x<<"  [x]補"<<endl;
	cout<<"+ "<<y<<"  "<<(on?"[y]補":"[-y]補")<<endl;
	cout<<"----------------"<<endl;
	cout<<" "<<(flag?"":" ")<<result<<"  "<<(on?"[x+y]補":"[x-y]補")<<endl;
	cout<<"即 x+y =";
	bu_code2true_value(result);
}
int main(){
	Number X = new Integer();
	Number Y = new Integer();
	cout<<"請輸入"<<MIN<<"~+"<<MAX<<"範圍内的帶符号十進制整數:";
	cin>>X->input;
	cout<<"請輸入"<<MIN<<"~+"<<MAX<<"範圍内的帶符号十進制整數:";
	cin>>Y->input;
	prepare(X);  // 準備
	tranfer(X);  // 轉換為二進制
	ToYuan_code(X);    // 轉換原碼
	ToBu_code(X);    // 轉換補碼 [X]補
	prepare(Y);  // 準備
	tranfer(Y);  // 轉換為二進制
	ToYuan_code(Y);    // 轉換原碼
	ToBu_code(Y);    // 轉換補碼 [X]補
	ToBetweenBu_code(Y);   // 轉換負的補碼  [-X]補 
	Add(X->bu_code,Y->bu_code,true);   // 實作加法
	cout<<endl;
	cout<<endl;
	cout<<endl;
	Add(X->bu_code,Y->between_bu_code,false);  // 實作減法
	free(X);
	free(Y);
	return 0;
}
           

5. 效果圖

(實驗三)補碼的加減法
(實驗三)補碼的加減法
(實驗三)補碼的加減法

繼續閱讀