文章目录
上接 (实验一)十进制整数的原反补移码实现
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/x-tex"> _原 </annotation> </semantics> </math>原 左移 1 位的结果是 2X = [10101100] <math> <semantics> <mrow> <msub> <mi mathvariant="normal"> 原 </mi> </msub> </mrow> <annotation encoding="application/x-tex"> _原 </annotation> </semantics> </math>原(丢了符号位后面的 1,最后面补了个 0),右移 1 位的结果是 <math> <semantics> <mrow> <mfrac> <mn> 1 </mn> <mn> 2 </mn> </mfrac> </mrow> <annotation encoding="application/x-tex"> \frac{1}{2} </annotation> </semantics> </math>21X = [10101011] <math> <semantics> <mrow> <msub> <mi mathvariant="normal"> 原 </mi> </msub> </mrow> <annotation encoding="application/x-tex"> _原 </annotation> </semantics> </math>原(丢了最后面的 0,符号位后面补了 0)
2. 反码移位
对于正数:左移或右移,符号位不变,数值位中,移出位丢弃,空出位补"0"([X] <math> <semantics> <mrow> <msub> <mi mathvariant="normal"> 反 </mi> </msub> </mrow> <annotation encoding="application/x-tex"> _反 </annotation> </semantics> </math>反 = [X] <math> <semantics> <mrow> <msub> <mi mathvariant="normal"> 原 </mi> </msub> </mrow> <annotation encoding="application/x-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/x-tex"> _反 </annotation> </semantics> </math>反 左移 1 位的结果是 2X = [10101101] <math> <semantics> <mrow> <msub> <mi mathvariant="normal"> 反 </mi> </msub> </mrow> <annotation encoding="application/x-tex"> _反 </annotation> </semantics> </math>反(丢了符号位后面的 1,最后面补了个 1),右移 1 位的结果是 <math> <semantics> <mrow> <mfrac> <mn> 1 </mn> <mn> 2 </mn> </mfrac> </mrow> <annotation encoding="application/x-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/x-tex"> _反 </annotation> </semantics> </math>反(丢了最后面的 0,符号位后面补了 1)
3. 补码移位
正负数由符号位决定,如 X = -0,[X] <math> <semantics> <mrow> <msub> <mi mathvariant="normal"> 补 </mi> </msub> </mrow> <annotation encoding="application/x-tex"> _补 </annotation> </semantics> </math>补 = 00000000,我们也把它当正数(嗯数(字)奸(细))
对于正数:左移或右移,符号位不变,数值位中,移出位丢弃,空出位补"1"([X] <math> <semantics> <mrow> <msub> <mi mathvariant="normal"> 反 </mi> </msub> </mrow> <annotation encoding="application/x-tex"> _反 </annotation> </semantics> </math>反 = [X] <math> <semantics> <mrow> <msub> <mi mathvariant="normal"> 补 </mi> </msub> </mrow> <annotation encoding="application/x-tex"> _补 </annotation> </semantics> </math>补嘛,原码补"0"我补码也补"0")
(还记得补码和原码的关系吗,当 X < 0,[X] <math> <semantics> <mrow> <msub> <mi mathvariant="normal"> 补 </mi> </msub> </mrow> <annotation encoding="application/x-tex"> _补 </annotation> </semantics> </math>补 = [X] <math> <semantics> <mrow> <msub> <mi mathvariant="normal"> 原 </mi> </msub> </mrow> <annotation encoding="application/x-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/x-tex"> _补 </annotation> </semantics> </math>补 左移 1 位的结果是 2X = [10101100] <math> <semantics> <mrow> <msub> <mi mathvariant="normal"> 反 </mi> </msub> </mrow> <annotation encoding="application/x-tex"> _反 </annotation> </semantics> </math>反(丢了符号位后面的 1,最后面补了个 0),右移 1 位的结果是 <math> <semantics> <mrow> <mfrac> <mn> 1 </mn> <mn> 2 </mn> </mfrac> </mrow> <annotation encoding="application/x-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/x-tex"> _反 </annotation> </semantics> </math>反(丢了最后面的 0,符号位后面补了 1)
3. 实现思路
1. 准备
包括:
- 检查输入是否合法,即第一个输入为"+“或”-",其后输入为数字
- 确定输入十进制的正负
- 将输入的 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. 效果图