1. 實驗要求
用C/C++程式設計實作兩個十進制整數(将其轉換成補碼)加、減運算結果,提示思想:模4補碼進行計算
2. 相關知識點
機器字長全部假設為 8 位,隻讨論整數,後不再特殊說明
0. 符号位
之前我們符号位都是用 1 位表示,模 4 補碼是用 2 位表示,對應關系如下:
符号位 | 表示 |
---|---|
00 | 正數 |
01 | 上溢 |
10 | 下溢 |
11 | 負數 |
特别提醒:補碼運算符号位作為數的一部分參與運算
1. 補碼加法
X + Y = [X] 補 _補 補 + [Y] 補 _補 補
- 符号位參與運算
- 如果有多出來的第三個符号位直接丢棄
- 運算結果也是補碼
- 最終将運算結果轉回真值
2. 補碼減法
X - Y = [X] 補 _補 補 + [-Y] 補 _補 補
規則同補碼加法
3. 實作思路
0. 準備
包括:
- 檢查輸入是否合法,即第一個輸入為"+“或”-",其後輸入為數字
- 确定輸入十進制的正負
- 将輸入的 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. 效果圖
