1. 实验要求
用C/C++编程实现两个十进制整数(将其转换成原码)乘法运算结果,提示思想:原码一位乘
2. 相关知识点
机器字长全部假设为 8 位,只讨论整数,后不再特殊说明
0. 移位
再次回忆原码的移位:左移右移都是符号位不变,数值位补“0”
1. 符号位
- 符号位取 1 位,0 为正,1 为负
- 符号位不直接参与乘法运算
- 结果的符号位 = 乘数符号位 ⊕ 被乘数符号位
2. 数值部分
-
操作数取绝对值
-
被乘数 x 乘数 = 积
-
部分积 = 两位全 0 符号位(高位) 拼接 全 0 被乘数数值位(低位)
-
积 = 部分积(高位) 拼接上 乘数的数值位(低位)
-
令乘数的最低位为判断位,若为 1,则加被乘数,否则加 0
-
累加后积右移一位,挤走乘数的最低位
-
重复上两步加和移位的操作,直到乘数的所有位全部被判断过(即乘数的数值部分有几位,就加、移位几次)
3. 实现思路
0. 准备
包括:
- 检查输入是否合法,即第一个输入为"+“或”-",其后输入为数字
- 确定输入十进制的正负
- 将输入的 string 转换为 int 类型
1. 转二进制
由准备工作准备好的 int 类型的数字直接转换为二进制,不过要注意两点,一是当数字为 0 时,取不到值,这时我们自己手动添个 0 上去,二是当数字为负数时,结果就很迷…解决方法是用其他变量暂存数字的绝对值
2. 转原码
由于取的全是绝对值,且积和乘数被乘数数值位数有关,所以原码就是二进制,不用再额外加 0
3. 计算
简化来看,原码一位乘就是循环的两个步骤,相加求和,移位
4. 相加求和
每次对 C 的最后一位进行判断,如果是 1,就 + A 一次,否则 + 0,同时更新 A 的值,并打印
5. 移位
移位操作也分两步,首先把 A 的最后一位插入到 C 的首位,截取 C 的总位数 - 1,得到 C 的移位结果,再把 A 的符号位之后插 0,截取 A 的总位数 -1,得到 A 的移位结果
4. 具体实现
#include<iostream>
#include<cmath> //abs
#include<malloc.h> //malloc
#include<algorithm> // reverse
#include<string> // string
#include<sstream> // stringstream
#include<cmath> // pow
#include "windows.h"
#define PLUS 1 // 正
#define MINUS -1 // 负
#define SYMBOLNUM 1 // 符号位长度
#define WORD_LENGTH 8 // 机器字长
#define MIN (-(1<<(WORD_LENGTH-SYMBOLNUM))+1) // 最小取值范围,注意取不到 -2^(n-1)了
#define MAX ((1<<(WORD_LENGTH-SYMBOLNUM))-1) // 最大取值范围
using namespace std;
typedef struct Integer *Number;
struct Integer{ // 定义一个结构体,把该数的各种码放进去
string input; // 输入的带符号十进制整数
string binary; // 二进制真值
string yuan_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){
//确定原码数值位
num->yuan_code += num->binary; // 原码数值位和二进制相等
}
// 输出每行的 A 和 C
void output(string A,string C,int len,bool yw){
cout<<(yw?"":" ")<<A;
// 填充格式的空格个数
for(int kg=A.size();kg<=11;kg++)
cout<<" ";
for(int i=0;i<len;i++)
cout<<C[i];
// 设置颜色为红色
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED);
for(int i=len;i<C.size();i++)
cout<<C[i];
cout<<endl;
// 设置回原来的青色
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_GREEN |
FOREGROUND_BLUE);
}
// 加法实现
void Add_method(string &A,string B){
int jw = 0; // 进位
for(int j=A.size()-1;j>=0;j--){
A[j] = (A[j]-'0')+(B[j]-'0')+jw;
if(A[j]>=2){
jw = 1;
A[j]-=2;
}else{
jw =0;
}
A[j] += '0';
}
}
void Add(string &A,string &B,string &C,int i){
if(C[C.size()-1] == '1'){ // 如果最低位是 1
cout<<"+|X| "<<B<<endl;
// 分割线
cout<<"-----------------------------"<<endl;
// 得到结果
Add_method(A,B);
output(A,C,i,false);
}else if(C[C.size()-1] == '0'){ // 如果最低位是 0
cout<<"+ 0 ";
for(int j=0;j<B.size();j++)
cout<<"0";
cout<<endl;
// 分割线
cout<<"-----------------------------"<<endl;
// 得到结果
output(A,C,i,false);
}
}
// 移位操作
void shift(string &A,string &C,int i){
// 先把 C 移一位
// 在 C 最前面插入 A 的最后一位,再从 0 开始截取 C 的长度-1
C.insert(0,A.substr(A.size()-1));
C = C.substr(0,C.size()-1);
// 再移动 A
// 把 A 符号位后插入 0,再从符号位后(1) 开始截取 A 的长度-1
A.insert(1,"0");
A = A.substr(0,A.size()-1);
cout<<">> ";
output(A,C,i+1,true);
}
// 初始化
void Init(string &A,string &B,string &C,Number X,Number Y){
A = "00"; // 存放积的高位
B = X->yuan_code; // 被乘数
C = Y->yuan_code; // 乘数,存放积的低位
// 被乘数 x 乘数 = 积
// 输出提示信息
cout<<" |X| = "<<B<<"→B, |Y| = "<<C<<"→C, 0→A"<<endl;
// 把A的位数补足(两位符号位00 + X 的数值位)
for(int i=0;i<X->yuan_code.size();i++)
A +="0";
B = "00"+B; // 加上符号位
cout<<" 部分积A 乘数C 被乘数B "<<B<<endl;
output(A,C,0,false);
}
// 符号位
void getSymobl(Number X,Number Y,string &symbol){
cout<<"符号位:";
// 如果两符号位相等,为正
if(X->symbol == Y->symbol){
if(X->symbol == PLUS)
cout<<PLUS<<"⊕"<<PLUS<<"=0"<<endl;
else
cout<<"0⊕0=0"<<endl;
symbol = "0";
}
else{ // 否则为负
if(X->symbol == PLUS)
cout<<PLUS<<"⊕0=1"<<endl;
else
cout<<"0⊕1=1"<<endl;
symbol = "1";
}
}
// 结果
void result(string A,string C,string symbol){
// 把 A 前面的 0 去掉
for(int i=0;i<A.size();i++)
if(A[i]=='1'){
A = A.substr(i);
break;
}
// 得到积
string result = A+C;
cout<<"[X × Y]原 = "<<symbol<<result<<endl;
// 符号位从 0 1 转变为 - 或者空
symbol = ((symbol=="1")?"-":"");
cout<<"即:X × Y = "<<symbol<<result<<" = ";
int sum = 0;
// 逆转,方便转回真值
reverse(result.begin(),result.end());
for(int i=0;i<result.size();i++)
if(result[i]=='1')
sum += pow(2.0,i);
cout<<symbol<<sum<<endl;
}
// 原码一位乘
void mul(Number X,Number Y){
string A;
string B;
string C;
string symbol;
// 初始化 A B C,并打印相关提示信息
Init(A,B,C,X,Y);
// 加法和移位的次数和乘数的数值位长度相等
for(int i=0;i<Y->yuan_code.size();i++){
// 先 +
Add(A,B,C,i);
// 再移位
shift(A,C,i);
}
cout<<endl;
// 得到符号位
getSymobl(X,Y,symbol);
// 得到最终答案
result(A,C,symbol);
}
int main(){
Number X = new Integer();
Number Y = new Integer();
// 颜色设置为青色
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_GREEN |
FOREGROUND_BLUE);
cout<<"请输入"<<MIN<<"~+"<<MAX<<"范围内的带符号十进制整数:";
cin>>X->input;
cout<<"请输入"<<MIN<<"~+"<<MAX<<"范围内的带符号十进制整数:";
cin>>Y->input;
prepare(X); // 准备
tranfer(X); // 转换为二进制
ToYuan_code(X);
prepare(Y); // 准备
tranfer(Y); // 转换为二进制
ToYuan_code(Y);
mul(X,Y);
free(X);
free(Y);
return 0;
}
5. 效果图