main函数是空的,内容都被写进各个子函数里。在这里稍微说一下各个运算的原理:
加法:
写了三种加法,分别是严格格式(bin_plus_limit),右对齐(bin_plus_right),和左对齐(bin_plus_left)。只有严格格式有溢出检测。加法的原理都是最基础的进位。
减法:
即对减数B补码做加法。调用的是右对齐加法。
加减法不分原码或补码。
乘法(原码):
- 先初始化一个部分积S,长度为输入码长的2倍。
部分积前半部分为0,后半部分为乘数B。
假设被乘数A=001011,被乘数B=000101
- 进入循环:
观察S的最后一位
- 如果是1,则先给S左对齐地加上A,再将S右移1位。
- 如果是0,则单纯将S右移1位。
- 当部分积S中整个B都向右移干净时,循环过程结束。
最后应指出,符号位独立于数值计算。做一个异号得负同号得正的判断即可。
乘法(补码):
乘法的补码运算本质上和原码是一样的,但是在循环判别上稍稍复杂了一点。本算法即Booth算法。
- 对被乘数A和乘数B都要进行一些处理。
- 如果A符号位只有1位,需要拓展为2位符号位
- B末尾必须加一位0(必需)
- 计算出-A的补码
- (与原码乘法相同)初始化一个前部分为0,后部分为B的部分积S
- 进入循环:
- 如果S[bit-1]-S[bit-2]==-1
(这里bit指的是S合并后的总位数,并不是给定数的位数)则先给S-A(加-A的补码),再将S向右位移1位。 - 如果S[bit-1]-S[bit-2]==0,则只把S向右位移1位。
- 如果S[bit-1]-S[bit-2]==1,则先给S+A,再把S向右位移1位。
- 循环到B还剩最后1位的时候,循环结束,最后再把S向右位移1位,得到最终结果
除法(整数/原码):
因为是整数定点机,所以除法结果不能出现小数。我们要限定,A>B才能A/B。
如上图所示,对于本来就位数相等的两个数,可以直接做除法。但是对于位数不等的两个数,必须先经历一个“左对齐做减法”的步骤,如下图
为了解决这个问题,我采用的办法是,对B进行填充,使之与A有效位相同,如下图
我们设这个扩充后的B为opB(option 操作)
此时做opA=A-opB,>0,如下图
opA就是操作过的A的意思,也就是这一步的余数。
之后我们让B向右位移1位,再做opA=opA-opB。明显这次<0
我们不对opA操作,继续把opB右移1位,如图
到此为止,opB==B,循环结束。即10111/100=101…11
总结刚刚的做法
- 给B进行处理(opB),删除左边多余的0(保留符号位的位置),右边填上0
- 进入循环:
- 如果opA-opB<0,则对opA不操作,对opB右移1位。上商为0。
- 如果opA-opB>0,则opA=opA-opB,对opB右移1为,上商为1。
- 在opB==B的时候,循环终止。得到答案
最后应指出,符号位独立于数值计算。做一个异号得负同号得正的判断即可。
除法(小数/原码):
小数的除法与整数的除法原理上是一样的,但是也有不同。
首先,因为是小数定点机,所以除法结果不能出现整数。我们要限定,A<B才能A/B。
因为小数从0.XXX开始,其左端是自然对齐的,不需要人工对齐。
但是小数有另外一个问题:至少需要一个双倍长度的寄存器才能存下余数,因为余数会比数值本身长。如下图
如果我们要完整保存余数,需要保存0.00000111。这里我们采用一种比较巧的办法:
把之前对opB右移改为对opA左移!!
这样我们只需要运算下面框选区域即可,但是这样不能得到一个正确的余数
如图,我们最终得到的余数是00111,是不正确的。
具体的操作办法我们总结如下
- 如果opA-opB<0,则仅对opA左移一位,上商0
- 如果opA-opB>0,则opA=opA-opB,再对opA左移一位,上商1
- 直到把B全部遍历一遍结束。
符号位独立于数值计算。做一个异号得负同号得正的判断即可。
最后要注意的是,刚刚的描述里有个细节:要判断opA-opB的大小,得计算一遍。这里又分了两个办法:
-
先opA=opA-opB,如果<0,再opA=opA+opB,这个就是恢复法
-
考虑到如果<0,opA=2*(opA+opB)-opB=2opA+opB,而如果>0,opA=2opA-opB,达成了对称性,所以可以减少恢复的次数,这个就是不恢复法。
最后代码如下:代码比较长,里面函数的含义都在注释里,主函数留给读者自行发挥
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXSIZE 64
//名称说明:bin是指不区分补码或原码的二进制码
//补码称code 原码称truecode
//整数称int 小数称dec
char* bin_plus_right(char* A, char* B);
char* bin_plus_left(char* A,char* B);
void bin_move(char* A, int n, int type,int signal);
//对二进制字符串某位置反
void reverse_digit(char* code, int digit){
if(code[digit]=='0'){
code[digit]='1';
}else if(code[digit]=='1'){
code[digit]='0';
}
}
//2的幂
int power_by_2(int subnum){
int res=2;
while((--subnum)>0){
res=2*res;
}
return res;
}
//由补码 输出十进制整数
int code_to_int(char* code,int signal){
//不能改变传来的code指针
int bit=strlen(code);
char* modecode=malloc((bit+1)*sizeof(char));
strcpy(modecode,code);
//生成补码的反码
int weight=1;
if(modecode[signal-1]=='1'){
weight=-1;
for(int i=signal;i<bit;i++){
reverse_digit(modecode,i);
}
}
//生成补码的补码(原码)
char* one="1";
if(modecode[signal-1]=='1'){
modecode=bin_plus_right(modecode,one);
}
//由原码生成十进制数
int flag=1;
int res=0;
int i=bit-1;
while(i>signal-1){
res+=(modecode[i--]-'0')*flag;
flag*=2;
}
if((res==0)&&(weight==-1)){
res=power_by_2(bit-signal);
}
res*=weight;
free(modecode);
return res;
}
//由原码 输出十进制整数
int truecode_to_int(char* code,int signal){
int bit=strlen(code);
int weight=1;
if(code[0]=='1'){
weight=-1;
}
int flag=1;
int res=0;
int i=bit-1;
while(i>signal-1){
res+=(code[i--]-'0')*flag;
flag*=2;
}
res*=weight;
return res;
}
//由原码 输出十进制小数
double truecode_to_dec(char* code,int signal){
int bit=strlen(code);
int weight=1;
if(code[0]=='1'){
weight=-1;
}
int i=1;
double res=0;
double flag=0.5;
while(i<bit){
res+=(code[i++]-'0')*flag;
flag/=2;
}
res*=weight;
return res;
}
//十进制 整数输出其补码
char* int_to_code(int num,int bit,int signal){
//num 十进制整数 bit 总位数 signal 符号位(算在总位数内)(一般为1)
char* modecode=(char*)malloc((bit+1)*sizeof(char));
//符号位及最后一位处理
if(num>=0){
for(int i=0;i<=signal-1;i++){
modecode[i]='0';
}
}else{
for(int i=0;i<=signal-1;i++){
modecode[i]='1';
}
num*=-1;
}
modecode[bit]='\0';
//生成原码
int i=bit-1;
while(i>=signal){
modecode[i--]=num%2+'0';
num/=2;
}
if(num!=0){
printf("<Error>:输入溢出,输入数±%d\n",power_by_2(bit-signal)-1);
}
//生成反码
if(modecode[signal-1]=='1'){
for(int i=signal;i<bit;i++){
reverse_digit(modecode,i);
}
}
//生成补码
char* one="1";
if(modecode[signal-1]=='1'){
modecode=bin_plus_right(modecode,one);
}
return modecode;
}
//给一个补码求其负数的补码
char* code_to_negative(char* code){
//不能改变传来的code指针
int bit=strlen(code);
char* modecode=malloc((bit+1)*sizeof(char));
strcpy(modecode,code);
int i=bit-1;
while(modecode[i--]!='1'){
;
}
while(i>=0){
reverse_digit(modecode,i--);
}
return modecode;
}
//原码乘法
char* truecode_multi(char* A,char* B,int bit,int signal){
//A*B A:被乘数 B:乘数
char* res=malloc((2*bit+1)*sizeof(