上接 (实验一)十进制整数的原反补移码实现

1. 实验要求

​ 用C/C++编程实现输入一个带符号十进制数,显示其原、反、补三种机器数左、右移2位的结果

2. 相关知识点

机器字长全部假设为 8 位,只讨论整数,后不再特殊说明

0. 移位运算

​ 对于带符号数的移位运算,符号位不变,只改变数值位,移位又分为左移和右移,左移一位相当于该数乘以 2,右移一位相当于该数除以 2

1. 原码移位

​ 对于正数:左移或右移,符号位不变,数值位中,移出位丢弃,空出位补 “0”

​ 对于负数:左移或右移,符号位不变,数值位中,移出位丢弃,空出位补 “0”

​ 总结:甭管正负,补 “0” 就对了

​ 例子:X = [11010110] <math> <semantics> <mrow> <msub> <mi mathvariant="normal"> 原 </mi> </msub> </mrow> <annotation encoding="application&#47;x&#45;tex"> _原 </annotation> </semantics> </math> 左移 1 位的结果是 2X = [10101100] <math> <semantics> <mrow> <msub> <mi mathvariant="normal"> 原 </mi> </msub> </mrow> <annotation encoding="application&#47;x&#45;tex"> _原 </annotation> </semantics> </math>(丢了符号位后面的 1,最后面补了个 0),右移 1 位的结果是 <math> <semantics> <mrow> <mfrac> <mn> 1 </mn> <mn> 2 </mn> </mfrac> </mrow> <annotation encoding="application&#47;x&#45;tex"> \frac{1}{2} </annotation> </semantics> </math>21X = [10101011] <math> <semantics> <mrow> <msub> <mi mathvariant="normal"> 原 </mi> </msub> </mrow> <annotation encoding="application&#47;x&#45;tex"> _原 </annotation> </semantics> </math>(丢了最后面的 0,符号位后面补了 0)

2. 反码移位

​ 对于正数:左移或右移,符号位不变,数值位中,移出位丢弃,空出位补"0"([X] <math> <semantics> <mrow> <msub> <mi mathvariant="normal"> 反 </mi> </msub> </mrow> <annotation encoding="application&#47;x&#45;tex"> _反 </annotation> </semantics> </math> = [X] <math> <semantics> <mrow> <msub> <mi mathvariant="normal"> 原 </mi> </msub> </mrow> <annotation encoding="application&#47;x&#45;tex"> _原 </annotation> </semantics> </math>嘛,原码补"0"我反码也补"0")

​ 对于负数:左移或右移,符号位不变,数值位中,移出位丢弃,空出位补"1"(既然原码负数都补"0",***的反码肯定都补"1")

​ 总结:正补"0"负补"1"

​ 例子:X = [11010110] <math> <semantics> <mrow> <msub> <mi mathvariant="normal"> 反 </mi> </msub> </mrow> <annotation encoding="application&#47;x&#45;tex"> _反 </annotation> </semantics> </math> 左移 1 位的结果是 2X = [10101101] <math> <semantics> <mrow> <msub> <mi mathvariant="normal"> 反 </mi> </msub> </mrow> <annotation encoding="application&#47;x&#45;tex"> _反 </annotation> </semantics> </math>(丢了符号位后面的 1,最后面补了个 1),右移 1 位的结果是 <math> <semantics> <mrow> <mfrac> <mn> 1 </mn> <mn> 2 </mn> </mfrac> </mrow> <annotation encoding="application&#47;x&#45;tex"> \frac{1}{2} </annotation> </semantics> </math>21X = [11101011] <math> <semantics> <mrow> <msub> <mi mathvariant="normal"> 反 </mi> </msub> <mi mathvariant="normal"> ​ </mi> </mrow> <annotation encoding="application&#47;x&#45;tex"> _反​ </annotation> </semantics> </math>(丢了最后面的 0,符号位后面补了 1)

3. 补码移位

​ 正负数由符号位决定,如 X = -0,[X] <math> <semantics> <mrow> <msub> <mi mathvariant="normal"> 补 </mi> </msub> </mrow> <annotation encoding="application&#47;x&#45;tex"> _补 </annotation> </semantics> </math> = 00000000,我们也把它当正数(嗯数(字)奸(细))

​ 对于正数:左移或右移,符号位不变,数值位中,移出位丢弃,空出位补"1"([X] <math> <semantics> <mrow> <msub> <mi mathvariant="normal"> 反 </mi> </msub> </mrow> <annotation encoding="application&#47;x&#45;tex"> _反 </annotation> </semantics> </math> = [X] <math> <semantics> <mrow> <msub> <mi mathvariant="normal"> 补 </mi> </msub> </mrow> <annotation encoding="application&#47;x&#45;tex"> _补 </annotation> </semantics> </math>嘛,原码补"0"我补码也补"0")

​ (还记得补码和原码的关系吗,当 X < 0,[X] <math> <semantics> <mrow> <msub> <mi mathvariant="normal"> 补 </mi> </msub> </mrow> <annotation encoding="application&#47;x&#45;tex"> _补 </annotation> </semantics> </math> = [X] <math> <semantics> <mrow> <msub> <mi mathvariant="normal"> 原 </mi> </msub> </mrow> <annotation encoding="application&#47;x&#45;tex"> _原 </annotation> </semantics> </math> 自低位向高位,尾数的第一个 1 及其右边的 0 保持不变,左位的各位取反)

​ 对于负数:当左移时,符号位不变,数值位中,空出位在最右边,肯定算在最低位中,补"0"妥妥的,当右移时,符号位不变,空出位在(除符号位)最左边,因为已经除去 -0 这个数奸,所以中间至少会遇到一个 1,所以空出位补"1"。

​ 总结:正数补"0",负数左移补"0"右移补"1"

​ 例子:X = [11010110] <math> <semantics> <mrow> <msub> <mi mathvariant="normal"> 补 </mi> </msub> </mrow> <annotation encoding="application&#47;x&#45;tex"> _补 </annotation> </semantics> </math> 左移 1 位的结果是 2X = [10101100] <math> <semantics> <mrow> <msub> <mi mathvariant="normal"> 反 </mi> </msub> </mrow> <annotation encoding="application&#47;x&#45;tex"> _反 </annotation> </semantics> </math>(丢了符号位后面的 1,最后面补了个 0),右移 1 位的结果是 <math> <semantics> <mrow> <mfrac> <mn> 1 </mn> <mn> 2 </mn> </mfrac> </mrow> <annotation encoding="application&#47;x&#45;tex"> \frac{1}{2} </annotation> </semantics> </math>21X = [11101011] <math> <semantics> <mrow> <msub> <mi mathvariant="normal"> 反 </mi> </msub> <mi mathvariant="normal"> ​ </mi> </mrow> <annotation encoding="application&#47;x&#45;tex"> _反​ </annotation> </semantics> </math>(丢了最后面的 0,符号位后面补了 1)

3. 实现思路

1. 准备

包括:

  1. 检查输入是否合法,即第一个输入为"+“或”-",其后输入为数字
  2. 确定输入十进制的正负
  3. 将输入的 string 转换为 int 类型

2. 转二进制

​ 由准备工作准备好的 int 类型的数字直接转换为二进制,不过要注意两点,一是当数字为 0 时,取不到值,这时我们自己手动添个 0 上去,二是当数字为负数时,结果就很迷…解决方法是用其他变量来取数字的绝对值

3. 实现原码移位

​ 原码左移符号位不变,数值位前两位被挤掉,后两位补 0,即截取符号位和后 5 位,末尾补 0;右移符号位不变,数值位最后两位被挤掉,前两位补 0,即原码第一位后补 0

4. 实现反码移位

​ 同原码,不过要判断符号位,如果是负数,补 1

5. 实现补码移位

​ 同原码,也要判断符号位,如果是右移,且是负数,补 1

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 MOVENUM 2 // 移动位数为 2
#define WORD_LENGTH 8 // 机器字长
#define MIN (-(1<<(WORD_LENGTH-1))) // 最小取值范围
#define MAX ((1<<(WORD_LENGTH-1))-1) // 最大取值范围
using namespace std;
typedef struct Integer *Number;
struct Integer{        // 定义一个结构体,把该数的各种码放进去
	string input;  // 输入的带符号十进制整数
	string binary;  // 二进制真值
	string yuan_code;   // 原码
	string fan_code;  // 反码
	string bu_code; // 补码
	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(0 < num->symbol)   // 如果小于 0,符号位为 1
		num->yuan_code += "0";
	else     // 否则大于 0 ,符号位为 0
		num->yuan_code += "1";
	//确定原码数值位
	num->yuan_code += num->binary;  // 原码数值位和二进制相等
	//补充中间的 0
	while(num->yuan_code.size() < WORD_LENGTH)   
		num->yuan_code.insert(1,"0");   // 在符号位后面追加 0
}
// 转换反码
void ToFan_code(Number num){
	if(num->symbol == PLUS ){  // 如果是正数,反码 = 原码
		num->fan_code = num->yuan_code;
		return;
	}
	// 如果输入的是MIN,只有补码能表示,反码置为 ——
	if(num->decimal == MIN){
		num->fan_code = "——";
		return;
	}
	// 符号位
	num->fan_code += "1";
	//确定反码数值位
	for(int i=0;i<num->binary.size();i++){
		if(num->binary[i]=='0')
			num->fan_code += "1";
		else
			num->fan_code += "0";
	}
	//补充中间的 1
	while(num->fan_code.size() < WORD_LENGTH)   
		num->fan_code.insert(1,"1");   // 在符号位后面追加 1
}

// 转换补码
void ToBu_code(Number num){
	if(num->symbol == PLUS){    // 如果是正数,补码 = 原码
		num->bu_code = num->yuan_code;
		return;
	}

	// 如果输入的是MIN,只有补码能表示
	if(num->decimal == MIN){
		num->bu_code += "1";
		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>0;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)
		num->bu_code.insert(0,"1");
	else
		num->bu_code.insert(0,"0");
}
// 转移码
void toYi_code(Number num){
	// 取得补码的全部位
	num->yi_code = num->bu_code;
	// 替换符号位
	if(num->bu_code[0] =='0') 
		num->yi_code.replace(0,1,"1");
	else
		num->yi_code.replace(0,1,"0");
}
// 原码移位
void MoveYuan_code(Number num){
	// 左移符号位不变,数值位前两位被挤掉,后两位补 0
	// 取出符号位
	string sign = num->yuan_code.substr(0,1);   // 从下标 0 开始,取 1 个长度
	// 取出数值位后 WORD_LENGTH - MOVENUM -1 (总长 - 移动位数 - 字符位)位
	string value = num->yuan_code.substr(MOVENUM+1);  // 取到末尾
	// 最后 MOVENUM 位补 0
	string leftMove = sign + value;
	for(int i=0;i<MOVENUM;i++)
		leftMove += "0";
	cout<<"左移"<<MOVENUM<<"位结果是:"<<leftMove<<" ";

	// 右移符号位不变,数值位后两位被挤掉,前两位补 0
	// 取出包括符号位在内的前 WORD_LENGTH - MOVENUM 位
	string rightMove = num->yuan_code.substr(0,WORD_LENGTH - MOVENUM);
	// 符号位后补 0
	for(int i=0;i<MOVENUM;i++)
		rightMove.insert(1,"0");
	cout<<"右移"<<MOVENUM<<"位结果是:"<<rightMove<<endl;
}
// 反码移位
void MoveFan_code(Number num){
	// 左移符号位不变,数值位前两位被挤掉,后两位补 0 或 1
	// 取出符号位
	string sign = num->fan_code.substr(0,1);   // 从下标 0 开始,取 1 个长度
	// 取出数值位后 WORD_LENGTH - MOVENUM -1 (总长 - 移动位数 - 字符位)位
	string value = num->fan_code.substr(MOVENUM+1);  // 取到末尾
	// 最后 MOVENUM 位补 0
	string leftMove = sign + value;
	for(int i=0;i<MOVENUM;i++){
		if(num->symbol == PLUS)   // 如果是正数,同原码,补 0
			leftMove += "0";
		else             // 否则补 1
			leftMove += "1";
	}
	cout<<"左移"<<MOVENUM<<"位结果是:"<<leftMove<<" ";

	// 右移符号位不变,数值位后两位被挤掉,前两位补 0
	// 取出包括符号位在内的前 WORD_LENGTH - MOVENUM 位
	string rightMove = num->fan_code.substr(0,WORD_LENGTH - MOVENUM);
	// 符号位后补 0 或 1
	for(int i=0;i<MOVENUM;i++){
		if(num->symbol == PLUS)   // 如果是正数,同原码,补 0
			rightMove.insert(1,"0");
		else             // 否则补 1
			rightMove.insert(1,"1");
	}
	cout<<"右移"<<MOVENUM<<"位结果是:"<<rightMove<<endl;
}
// 补码移位
void MoveBu_code(Number num){
	// 左移符号位不变,数值位前两位被挤掉,后两位补 0
	// 取出符号位
	string sign = num->yuan_code.substr(0,1);   // 从下标 0 开始,取 1 个长度
	// 取出数值位后 WORD_LENGTH - MOVENUM -1 (总长 - 移动位数 - 字符位)位
	string value = num->yuan_code.substr(MOVENUM+1);  // 取到末尾
	// 最后 MOVENUM 位补 0
	string leftMove = sign + value;
	// 补码无论正负左移补 0 
	for(int i=0;i<MOVENUM;i++)
		leftMove += "0";
	cout<<"左移"<<MOVENUM<<"位结果是:"<<leftMove<<" ";

	// 右移符号位不变,数值位后两位被挤掉,前两位补 0
	// 取出包括符号位在内的前 WORD_LENGTH - MOVENUM 位
	string rightMove = num->yuan_code.substr(0,WORD_LENGTH - MOVENUM);
	// 符号位后补 0 或 1
	// 当是负数,补码补 1
	for(int i=0;i<MOVENUM;i++){
		if(num->symbol == PLUS)   // 如果是正数,同原码,补 0
			rightMove.insert(1,"0");
		else             // 否则补 1
			rightMove.insert(1,"1");
	}
	cout<<"右移"<<MOVENUM<<"位结果是:"<<rightMove<<endl;
}
int main(){
	Number num = new Integer();
	cout<<"请输入"<<MIN<<"~+"<<MAX<<"范围内的带符号十进制整数:";
	cin>>num->input;
	prepare(num);  // 准备
	tranfer(num);  // 转换为二进制
	ToYuan_code(num);    // 转换成原码
	ToFan_code(num);   // 转换成反码
	ToBu_code(num);    // 转换成补码
	toYi_code(num);  // 转换移码
	cout<<"输入的数是:"<<num->input<<endl;
	cout<<"其原码表示为:"<<num->yuan_code<<" ";
	MoveYuan_code(num);
	cout<<"其反码表示为:"<<num->fan_code<<" ";
	MoveFan_code(num);
	cout<<"其补码表示为:"<<num->bu_code<<" ";
	MoveBu_code(num);
	free(num);
	return 0;
}

5. 效果图