1
0
mirror of https://github.com/Didnelpsun/CS408.git synced 2026-06-16 15:07:38 +08:00

更新链表与数据操作

This commit is contained in:
Didnelpsun
2021-08-21 23:58:48 +08:00
parent 3c864b55c1
commit 4b5bdc1edc
5 changed files with 264 additions and 191 deletions

View File

@@ -56,8 +56,10 @@ public:
bool Empty() const;
// 插入
virtual bool Insert(int index, element_type data) = 0;
// 循环插入
bool LoopInsert(element_type* elem, int start, int length);
// 插入
bool PriorInsert(element_type* elem, int start, int length);
// 后插入
bool NextInsert(element_type* elem, int start, int length);
};
class LinkListWithHead : public LinkList {
@@ -192,7 +194,7 @@ bool LinkListWithHead::Print() {
cout << "第0个元素值为空" << endl;
// 当前遍历指针
LinkListNode* p = this->GetNext();
if (p != nullptr) {
while (p != nullptr) {
cout << "" << i << "个元素值为" << p->GetData() << endl;
i++;
p = p->GetNext();
@@ -201,16 +203,16 @@ bool LinkListWithHead::Print() {
}
bool LinkListWithoutHead::Print() {
int i = 1;
int i = 0;
if (this->GetLength() == 0) {
return true;
}
cout << "" << i << "个元素值为" << this->GetData() << endl;
// 当前遍历指针
LinkListNode* p = this->GetNext();
if (p != nullptr) {
cout << "" << i << "个元素值为" << p->GetData() << endl;
while (p != nullptr) {
i++;
cout << "" << i << "个元素值为" << p->GetData() << endl;
p = p->GetNext();
}
return true;
@@ -245,82 +247,99 @@ bool LinkListWithHead::Insert(int index, element_type data) {
cout << "Insert:插入索引值过大!" << endl;
return false;
}
cout << i << index << endl;
// 此时i==index-1
// 将p原来的后继给新的结点
s->SetNext(p->GetNext());
p->SetNext(s);
cout << p->GetNext() << endl;
this->SetLength();
return true;
}
bool LinkListWithoutHead::Insert(int index, element_type data) {
return true;
}
bool LinkList::LoopInsert(element_type* elem, int start, int length) {
for (int i = 1; i < length; i++) {
bool result = this->Insert(i, elem[i + start]);
if (!result) {
cout << "LoopInsert:循环插入失败!" << endl;
return false;
}
if (index < 0) {
cout << "Insert:插入索引值过小!" << endl;
return false;
}
if (index == 0) {
LinkListNode* node = new LinkListNode(this->GetData());
this->SetData(data);
this->SetLength();
return true;
}
// 定义一个结点指针p指向当前扫描到的结点
LinkListNode* p;
// 定义一个变量i表示当前扫描到的结点的索引号
int i = 1;
// 将链表头结点的next指向p为第1个结点
p = this->GetNext();
LinkListNode* s = new LinkListNode(data);
// 如果该链表为空链表
if (p == nullptr) {
this->SetLength();
this->SetNext(s);
return true;
}
// 循环遍历到达指定索引号的单链表的结点
// 条件是当前结点的下一个不为空且索引号到达,所到达的结点一定不是空结点
while (p->GetNext() != nullptr && i < index - 1) {
p = p->GetNext();
i++;
}
// 如果此时i小于index-1表示遍历完还没有到达对应的索引
if (i < index - 1) {
cout << "Insert:插入索引值过大!" << endl;
return false;
}
// 此时i==index-1
// 将p原来的后继给新的结点
s->SetNext(p->GetNext());
p->SetNext(s);
this->SetLength();
return true;
}
//// 插入无头节点单链表元素
//// C语言也无法调用这个函数
//int InsertLinkListWithoutHead(LinkList list, int index, element_type elem) {
// if (index < 0) {
// cout << "InsertLinkListWithoutHead:插入索引值过小!" << endl;
// return false;
// }
// if (index == 0) {
// LinkListNode* s = (LinkListNode*)malloc(sizeof(LinkListNode));
// if (s) {
// s->data = elem;
// // 将s的后继设为list指针
// s->next = list;
// // 将list指针设置为s指针
// list = s;
// return true;
// }
// else {
// cout << "InsertLinkListWithoutHead:分配内存失败!" << endl;
// return false;
// }
// }
// // 定义一个结点指针p指向当前扫描到的结点
// LinkListNode* p;
// // 定义一个变量i表示当前扫描到的结点的索引号
// int i = 0;
// // 将链表头结点指向p为第0个结点
// p = list;
// // 循环遍历到达指定索引号的单链表的结点
// // 条件是当前结点的下一个不为空且索引号到达,所到达的结点一定不是空结点
// while (p->next != NULL && i < index - 1) {
// p = p->next;
// i++;
// }
// // 如果此时i小于index-1表示遍历完还没有到达对应的索引
// if (i < index - 1) {
// cout << "InsertLinkListWithoutHead:插入索引值过大!" << endl;
// return false;
// }
// // 此时i==index-1
// LinkListNode* s = (LinkListNode*)malloc(sizeof(LinkListNode));
// if (s) {
// s->data = elem;
// // 将p原来的后继给新的结点
// s->next = p->next;
// p->next = s;
// return true;
// }
// else {
// cout << "InsertLinkListWithoutHead:分配空间失败!" << endl;
// return false;
// }
//}
bool LinkList::PriorInsert(element_type* elem, int start, int length) {
if (this->GetType()) {
for (int i = 0; i < length; i++) {
bool result = this->Insert(1, elem[i + start]);
if (!result) {
cout << "PriorInsert:循环插入失败!" << endl;
return false;
}
}
return true;
}
else {
for (int i = 0; i < length; i++) {
bool result = this->Insert(0, elem[i + start]);
if (!result) {
cout << "PriorInsert:循环插入失败!" << endl;
return false;
}
}
return true;
}
}
bool LinkList::NextInsert(element_type* elem, int start, int length) {
if (this->GetType()) {
for (int i = 0; i < length; i++) {
bool result = this->Insert(i + 1, elem[i + start]);
if (!result) {
cout << "NextInsert:循环插入失败!" << endl;
return false;
}
}
return true;
}
else {
for (int i = 0; i < length; i++) {
bool result = this->Insert(i, elem[i + start]);
if (!result) {
cout << "NextInsert:循环插入失败!" << endl;
return false;
}
}
return true;
}
}

View File

@@ -21,12 +21,13 @@ int SequenceListTest() {
int LinkListTest() {
LinkListWithHead list;
//cout << list.Empty() << endl;
//element_type a[6] = { '1','2','3','4','5','6' };
//list.LoopInsert(a, 1, 6);
list.Insert(1, '3');
list.Print();
list.Insert(2, '4');
element_type a[6] = { '1','2','3','4','5','6' };
list.PriorInsert(a, 2, 3);
list.Print();
cout << list.GetLength() << endl;
LinkListWithoutHead list2;
list2.PriorInsert(a, 2, 3);
list2.Print();
cout << list2.GetLength() << endl;
return 0;
}
}

View File

@@ -173,7 +173,7 @@ $CRC$具有纠错能力,但是数据链路层只使用了其检错能力,错
$P_1$是第一位,即$0001$位,所以$1$在二进制位置的第四个地方,找到$10$个位置中所有二进制位置的第四个位为$1$的数据位,即$0011$$3$$D_1$$0101$$5$$D_2$$0111$$7$$D_4$$1001$$9$$D_5$。所以$P_1$可以校验这$4$个数据。
要使其能校验,就要令所有要校验的位异或等于$0$。
要使其能校验,就要令所有要校验的位异或等于$0$,或直接等于所有校验数据位异或值
即令$P_1\oplus D_1\oplus D_2\oplus D_4\oplus D_5=0$。从而$P_1\oplus1\oplus0\oplus1\oplus0=0$,根据同$0$异$1$得到$P_1=0$。

File diff suppressed because one or more lines are too long

View File

@@ -211,49 +211,51 @@ k|2|3|4|5|6|7
#### 有符号数
+ 定点整数:最高一位是符号位,0是正1是负,小数点位置一般隐含在最后。
+ 定点小数:最高一位是符号位,0是正1是负,小数点位置隐含在符号位后面。
+ 定点整数:最高一位是符号位,$0$是正,$1$是负,小数点位置一般隐含在最后。范围为$[-(2^n-1),2^n-1]$。
+ 定点小数:最高一位是符号位,$0$是正,$1$是负,小数点位置隐含在符号位后面。范围为$[-(1-2^{-n}),1-2^{-n}]$。
+ 数值部分也称为尾数。要保存一个非整数需要保存定点整数与定点小数两个部分。
#### 原码
+ 原码用尾数表示真值的绝对值符号位“0/1”对应“正/负”。
+ 若机器字长为n+1位则尾数占n位。
+ 若使用1B来保存数值则+19D就是0001 0011-19D就是1001 0011。
+ 有时1001 0011会写为1,0010011其中的逗号只是为了标注正负号本身是不存在的。
+ 若未指明机器字长,则最开头的多个0可以省略。如1001 0011可以表示为1,10011。
+ 同理小数也可以使用1.11表示,这是指-0.11。
+ 原码:用尾数表示真值的绝对值,符号位“$0/1$”对应“正/负”。
+ 若机器字长为$n+1$位,则尾数占$n$位。
+ 若使用$1B$来保存数值,则$+19D$就是$0001\,0011$$-19D$就是$1001\,0011$
+ 有时$1001\,0011$会写为$1,0010011$,其中的逗号只是为了标注正负号,本身是不存在的。
+ 若未指明机器字长,则最开头的多个$0$可以省略。如$1001\,0011$可以表示为$1,10011$
+ 同理小数也可以使用$1.11$表示,这是指$-0.11$而不是$1.11$的真值
+ 若机器字长$n+1$位,则原码整数的表示范围是$[-(2^n-1),2^n-1]$。
+ 若机器字长$n+1$位,则原码小数的表示范围是$[-(1-2^{-n}),1-2^{-n}]$。
+ 原码表示时真值0有+0和-0两种形式。
+ 原码表示时真值$0$有$+0$和$-0$两种形式。
#### 反码
+ 反码:若符号位为0,则反码与原码相同,若符号位为1,则数值位全部取反。
+ 反码:若符号位为$0$,则反码与原码相同,若符号位为$1$,则数值位全部取反。
+ 可以转换为原码再取反。
+ 若机器字长$n+1$位,则反码整数的表示范围是$[-(2^n-1),2^n-1]$。
+ 若机器字长$n+1$位,则反码小数的表示范围是$[-(1-2^{-n}),1-2^{-n}]$。
+ 反码表示时真值0有+0和-0两种形式。
+ 反码表示时真值$0$有$+0$和$-0$两种形式。
+ 反码只是由原码转换为补码的一个中间态,实际上并没有作用。
#### 补码
+ 补码:若符号位为0,则反码与原码相同,若符号位为1,则数值位全部取反再加一,即反码加一。
+ 补码表示时真值0只有一种形式0000 0000。
+ 多出来的一种形式1000 0000表示$-2^7$和$-1$。
+ 补码:若符号位为$0$,则反码与原码相同,若符号位为$1$,则数值位全部取反再加一,即反码加一。
+ 补码表示时真值$0$只有一种形式$0000\,0000$
+ 多出来的一种形式$1000\,0000$表示$-2^7$和$-1$。
+ 若机器字长$n+1$位,则补码整数的表示范围是$[-2^n,2^n-1]$。
+ 若机器字长$n+1$位,则补码小数的表示范围是$[-1,1-2^{-n}]$。
+ 将负数补码转回原码的方法相同:尾数取反,末位加一。补码的补码就是原码。
+ 如果已知一个数值的补码,那么求这个值的负数的补码就是全部位取反,末位加一。
+ 如果已知一个负数的补码,那个求这个值的原码就是数值位取反再加一,或是负数补码中,最右边的1以及右边不变,最右边的1的左边取反。
+ 如果已知一个负数的补码,那个求这个值的原码就是数值位取反再加一,或是负数补码中,最右边的$1$以及右边不变,最右边的$1$的左边取反。
+ 补码算术移位:将补码的符号位与数值位一起右移一位并保持原符号位的值不变,表示除二。
+ 变形补码:又称为模四部分,双符号位的补码小数,用$00$表示正,$11$表示负,用于完成算术运算的$ALU$部件中。
#### 移码
+ 补码的基础上将符号位取反。
+ 移码只能用于表示整数,而不能表示定点小数。
+ 补码的基础上将符号位取反。即在真值上加上一个常数,一般为$2^n$。
+ 移码只能用于表示整数,而不能表示定点小数。一般用来表示浮点数的阶码。
+ 若机器字长$n+1$位,则移码整数的表示范围是$[-2^n,2^n-1]$。
+ 若机器字长$n+1$位,则移码小数的表示范围是$[-1,1-2^{-n}]$。
+ 移码由于负数的最高位为0,正数的最高位为1,从而能更方便对比大小。
+ 移码由于负数的最高位为$0$,正数的最高位为$1$,从而能更方便对比大小。
机器数|无符号数|原码|反码|补码|移码
:----:|:------:|:--:|:--:|:--:|:--:
@@ -277,7 +279,7 @@ k|2|3|4|5|6|7
+ 由于计算机码操作若最高位进一就被舍弃,则天然是进行模运算,所以可以通过数学模运算来实现机器码的运算。
+ 带余除法:设$x,m\in Z$$m>0$则存在唯一决定的整数$q$和$r$,使得$x=q\cdot m+r\,,0\leqslant r<m$。
+ 若两个数绝对值之和为模,则互为补数。即模-数的绝对值=数的补数(正数)。从而数加上数的补数就得到了模。
+ 补码就是正数不变,负数取模的结果。如-66=- 0100 0010而(1000 0000 - 0100 0010)mod(1111 1111)=1011 1110也就是其补码。
+ 补码就是正数不变,负数取模的结果。如$-66=-0100\,0010$,而$(1000\,0000-0100\,0010)\mod(1111\,1111)=1011\,1110$,也就是其补码。
+ 从而就可以用补数的加法替代原码转换的减法。
+ 所以补码可以让减法操作转换为加法操作,减少硬件成本。
@@ -285,122 +287,98 @@ k|2|3|4|5|6|7
#### 定点数移位运算
+ 算术移位:通过改变各数码位和小数点的相对位置,从而改变各数码位的位权。可用移位运算实现乘法、除法。
+ 算术移位:通过改变各数码位和小数点的相对位置,从而改变各数码位的位权。可用移位运算实现乘法、除法。
+ 原码的算数移位——符号位保持不变,仅对数值位进行移位:
+ 若右移高位补0,低位舍弃,右移代表除2,若移出的是0则刚好整除,若移出的是1则会整除余1丢失精度。
+ 若左移低位补0位舍弃,左移代表乘2,若移出的是0则刚好乘2,若移出的是1,则会严重误差。
+ 反码的算术移位——正数的反码与原码相同,所以正数的处理跟原码一样。而对于负数而言,反码的1等于原码的0,反码的0等于原码的1
+ 若右移位补1位舍弃。
+ 若左移高位补1,低位舍弃。
+ 补码的算术移位——正数的补码与原码相同,所以正数的处理跟原码一样。由于负数补码=反码末位加一,导致反码最右边几个连续的1都因进位而变为0,直到进位碰到第一个0为止。所以得到规律:
+ 负数补码中,最右边的1及其右边同原码一样。最右边的1的左边同反码一样。
+ 右移同反码,高位补1,低位舍弃。
+ 左移同原码,低位补0,高位舍弃。
+ 若右移高位补$0$,低位舍弃,右移代表除$2$,若移出的是$0$则刚好整除,若移出的是$1$则会整除余$1$丢失精度。
+ 若左移低位补$0$,高位舍弃,左移代表乘$2$,若移出的是$0$则刚好乘$2$,若移出的是$1$,则会严重误差。
+ 反码的算术移位——正数的反码与原码相同,所以正数的处理跟原码一样。而对于负数而言,反码的$1$等于原码的$0$,反码的$0$等于原码的$1$
+ 若右移位补$1$,高位舍弃。
+ 若左移高位补$1$,低位舍弃。
+ 补码的算术移位——正数的补码与原码相同,所以正数的处理跟原码一样。由于负数补码=反码末位加一,导致反码最右边几个连续的$1$都因进位而变为$0$,直到进位碰到第一个$0$为止。所以得到规律:
+ 负数补码中,最右边的$1$及其右边同原码一样。最右边的$1$的左边同反码一样。
+ 右移同反码,高位补$1$,低位舍弃。
+ 左移同原码,低位补$0$,高位舍弃。
+ 逻辑移位,可以视为对无符号数的算术移位:
+ 逻辑右移:高位补0,低位舍弃。
+ 逻辑左移:低位补0,高位舍弃。
+ 逻辑右移:高位补$0$,低位舍弃。
+ 逻辑左移:低位补$0$,高位舍弃。
+ 循环移位,将移出的一位放到另一端的端点,类似队列:
+ 进位位$CF$:保存计算是否进位。$1$代表产生进位,$0$代表未产生进位。
+ 带$CF$就是大循环,不带$CF$的就是小循环。
+ 循环右移:将最低位的一位移出放到最高位,其余右移一位。
+ 循环左移:将最高位的一位移出放到最低位,其余左移一位。
+ 进位位CF保存计算是否进位。1代表产生进位0代表未产生进位。
+ 带进位位的循环左移:需要加上进位位数值的循环左移。
+ 带进位位的循环右移:需要加上进位位数值的循环右移。
+ 适合将数据的低字节数据和高字节数据互换。
#### 定点数加减运算
+ 注意机器字长,若溢出则将溢出位丢弃。
+ 原码的加减,由加法器和减法器两个硬件来实现,因为最高位为符号位,所以不能直接进行加减:
+ 原码的加法:
+ 正数+正数:绝对值做加法,结果为正数。
+ 负数+负数:绝对值做加法,结果为负数。
+ 正数+负数:绝对值大的减绝对值小的,符号同绝对值大的数。
+ 负数+正数:绝对值大的减绝对殖小的,符号同绝对值大的数。
+ 原码的减法,减数符号取反,转变为加法:
+ 正-负→正+正。
+ 负-正→负+负。
+ 正-正→正+负。
+ 负+正→负-负。
+ 负-负→负+正
+ 补码的加减,由于减法器的硬件实现比较困难,所以原码的减法操作可以由补码来更简单实现,不用考虑符号位的异常,直接全部参与运算:
+ 如设机器字长为8位含一位符号位A=15B=-24求A+B和A-B的补码
+ A=+1111从而原码补码为0000 1111B=-11000所以原码为1001 1000反码为1110 0111补码为1110 1000。
+ 所以A+B的补码等于A的补码加B的补码为0000 1111+1110 1000=1111 0111原码就是1000 1001即-9这与预期的一样
+ 同理负数值的补码就是补码全部取反加1所以A-B=0000 1111+0001 1000=0010 0111即+39
+ 参与运算的两个操作数均用补码表示
+ 按二进制运算规则运算,逢二进一。符
+ 号位与数值位按同样规则一起参与运算,符号位运算产生的进位要丢掉,结果的符号位由运算得出
+ 补码加减运算依据下面的公式进行。当参加运算的数是定点小数时,模$M=2$;当参加运算的数是定点整数时,模$M=2^{n+1}$。$[A+B]_\text{补}=[A]_\text{补}+[B]_\text{补}$$\mod M$$[A-B]_\text{补}=[A]_\text{补}+[-B]_\text{补}$$\mod M$
+ $\mod M$运算是为了将溢出位丢掉。也就是说,若做加法,则两数的补码直接相加;若做减法,则将被减数与减数的机器负数相加。
+ 补码运算的结果亦为补码。
+ 溢出判断,由于使用补码进行加减操作都会变成加法,所以只用考虑加法溢出的处理。
+ 小于最小值就是下溢。只有负数+负数才会下溢得到正数。如-24-124=1110 1000+1000 0100=0110 1100=108。
+ 大于最大值就是上溢。只有正数+正数才会上溢得到负数。如15+124=0000 1111+0111 1100=1000 1011=-117。
+ 小于最小值就是下溢。只有负数+负数才会下溢得到正数。如$-24-124=1110\,1000+1000\,0100=0110\,1100=108$
+ 大于最大值就是上溢。只有正数+正数才会上溢得到负数。如$15+124=0000\,1111+0111\,1100=1000\,1011=-117$
+ 方法一,采用一位符号位(模二补码),根据符号位判断:
+ 设A的符号为$A_s$B的符号为$B_s$,运算结果的符号为$S_s$。
+ 设$A$的符号为$A_s$$B$的符号为$B_s$,运算结果的符号为$S_s$。
+ 则溢出逻辑表达式为$V=A_sB_s\overline{S_s}+ \overline{A_s}\overline{B_s}S_s$(即$V=A_s\&\&B_s\&\&!(S_s)\mid\mid!(A_s)\&\&!(B_s)\&\&S_s$)。
+ 若$V=0$,表示无溢出,若$V=1$,表示有溢出。
+ 如-24-124=108产生了溢出$A_s=1$、$B_s=1$、$S_s=0$$V=A_sB_s\overline{S_s}+ \overline{A_s}\overline{B_s}S_s=1\,1\,1+0\,0\,0=1+0=1$,所以产生了溢出。
+ 方法二,采用一位符号位(模二补码),根据数据位进位1情况判断:
+ 如$-24-124=108$产生了溢出,$A_s=1$、$B_s=1$、$S_s=0$$V=A_sB_s\overline{S_s}+ \overline{A_s}\overline{B_s}S_s=1\,1\,1+0\,0\,0=1+0=1$,所以产生了溢出。
+ 方法二,采用一位符号位(模二补码),根据数据位进位$1$情况判断:
+ 符号位的进位$C_s=0$,最高数值位的进位$C_1=1$时产生了上溢。
+ 符号位的进位$C_s=1$,最高数值位的进位$C_1=0$时产生了下溢。
+ 如-24-124=1110 1000+1000 0100=0110 1100中符号位都为1,所以符号位进1$C_s=1$而最高数值位为1+0=1没有进位所以$C_1=0$,所以就产生了下溢。
+ 方法三采用双符号位模四补码正数符号为00负数符号为11。
+ 如$-24-124=1110\,1000+1000\,0100=0110\,1100$中符号位都为$1$相加结果为$0$,所以符号位进$C_s=1$,而最高数值位为$1+0=1$,没有进位,所以$C_1=0$,所以就产生了下溢。
+ 方法三,采用双符号位(模四补码),正数符号为$00$,负数符号为$11$
+ 若两个符号位不同,则表示溢出,第一个符号位表示应该得到的符号位,第二个符号位代表实际得到的符号位。
+ 如-24-124=11,110 1000+11,000 0100=10,110 1100=108。下溢。
+ 如15+124=00,000 1111+00,111 1100=01,000 1011=-117。上溢。
+ 如$-24-124=11,110\,1000+11,000\,0100=10,110\,1100=108$。下溢。
+ 如$15+124=00,000\,1111+00,111\,1100=01,000\,1011=-117$。上溢。
+ 实际存储时只存储一个符号位,运算时会复制一个符号位。
+ 符号扩展:防止溢出的一个方法就是将短数据扩展为长数据。
+ 整数扩展,在原符号位和数值位中间添加新位,正数都填充0,对于负数:
+ 原码:扩展补0
+ 反码:扩展补1
+ 补码:扩展补1
+ 小数扩展,在最后面添加新位,正数都填充0,对于负数:
+ 原码:扩展补0
+ 反码:扩展补1
+ 补码:扩展补0
+ 符号扩展:防止溢出的一个方法就是将短数据扩展为长数据。(补码高$1$低$0$
+ 整数扩展,在原符号位和数值位中间添加新位,正数都填充$0$,对于负数:
+ 原码:扩展补$0$
+ 反码:扩展补$1$
+ 补码:扩展补$1$
+ 小数扩展,在最后面添加新位,正数都填充$0$,对于负数:
+ 原码:扩展补$0$
+ 反码:扩展补$1$
+ 补码:扩展补$0$
#### 定点数乘法运算
对于原码的乘数运算可以参考十进制的乘数运算,将乘数一位一位的乘被乘数然后再全部相加得到的就是答案。而使用二进制的一位位乘法显然比十进制的一位位乘更简单。
##### 原码一位乘法
一般使用原码一位乘法,即每次只乘一位的数据
在原码乘法时,可以先符号位单独处理,将两个符号进行异或操作,得到的结果就是最后的结果的符号。然后对数据的绝对值(去除符号位)进行一位位的乘法(位积)然后相加
在运算器的组成时出现一个表格说明在进行乘运算时ACC保存乘积高位MQ保存乘数与乘积低位X保存被乘数
原码一位乘法机器实现时就是按照这种方式计算:
1. 字长若为n+1位则ACC、MQ、X全部初始化为n位将被乘数的绝对值放入X中MQ放入乘数的绝对值ACC初始化为全0
2. 将MQ的最右边的一位当做当前乘运算位让其进行乘运算运算规则是若当前位是1则ACC加上被乘数即ACC+=X若当前位是0则ACC加上0保持不变跳过
3. 将ACC和MQ的数据连接在一起全部逻辑右移一位ACC数据高位补0ACC最后一个低位移到MQ的最高位。将MQ的最后一位抛弃。若是第i轮逻辑右移则MQ的前i位是结果的后i个低位值。
4. 从步骤二开始重复字长若为n+1位则重复n次直到MQ的最后一位是符号位则停止计算。此时ACC的全部和MQ的前n位都是结果
5. 定点小数的小数位隐藏在符号位后面第一位定点正数的小数位隐藏在MQ符号位的前一位
6. 将两个符号位的异或结果赋值给积最高位。
**例题** 设机器字长为n+1=5位(含1位符号位其中x的原码为1.1101y的原码为0.1011采用原码一位乘法求xy
其中x就是-0.1101而y就是+0.1011。先抛去符号位就得到01101和01011两个数据。
将MQ、ACC、X都初始化为五位存储单元。X放入被乘数01101MQ放入乘数01011ACC为00000。
运算器结构ACC与MQ相连数据流是双向的ACC与ALU相连数据流是双向的X的数据流向ALU。
此时MQ=01011作为乘法单位的最后位为1所以ACC+=X从而=00000+01101=01101这就是第一个的位积。
由于按照乘法规则第二个位积计算时需要错位相加计算机的处理方式是ACC和MQ的数据连在一起全部逻辑右移一位左边补0
所以ACC的数据由01101变为00110最后的1移到MQ最高位MQ由01011变为10101最后的一位1溢出被抛弃代表这一位的位积已经计算并相加完成所以不用管了。此时结果的高位还在ACC中而结果的低位从ACC移到了MQ中在MQ的低位也不参与后面的运算所以也不用管了。
然后计算下一个最低位的位积此时MQ的最低位还是1所以ACC+=X=00110+01101=10011。
同样计算完后再错位进行逻辑右移ACC由10011变成了01001MQ由10101变成了11010最低位的1被抛弃此时MQ中已经有两个结果最低位。
此时MQ最低位为0所以ACC保持不变再逻辑右移一位ACC由01001变为00100MQ由11010变为11101抛弃一位0MQ有三个结果最低位。
此时MQ最低位为1则ACC+=X=00100+01101=10001。
同样计算完后再错位进行逻辑右移ACC由10001变成了01000MQ由11101变成了11110最低位的1被抛弃此时MQ中已经有四个结果最低位此时MQ最低位是代表符号的0不参与运算。此时计算已经结束。
小数的小数点隐藏在第一位的后面所以此时结果在ACC和MQ的前四位中即0.10001111。最后加上符号异或结果得到1.10001111。
##### 补码一位乘法
对于补码的乘法运算的逻辑也跟原码的类似补码的计算就是使用Booth算法实现
+ 原码一位乘法
+ 一般使用原码一位乘法,即每次只乘一位的数据。
+ 在原码乘法时,可以先符号位单独处理,将两个符号进行异或操作,得到的结果就是最后的结果的符号。然后对数据的绝对值(去除符号位)进行一位位的乘法(位积)然后相加
+ 由于运算时可能存在绝对值大于$1$但是不是溢出的情况,所以部分积和被乘数使用双符号位。
+ 在运算器的组成时出现一个表格,说明在进行乘运算时,$ACC$保存乘积高位,$MQ$保存乘数与乘积低位,$X$保存被乘数
+ 原码一位乘法机器实现时就是按照这种方式计算:
1. 字长若为$n+1$位,则$ACC$、$MQ$、$X$全部初始化为$n$位,将被乘数的绝对值放入$X$中,$MQ$放入乘数的绝对值,$ACC$初始化为全$0$
2. 将$MQ$的最右边的一位当做当前乘运算位,让其进行乘运算,运算规则是,若当前位是$1$,则$ACC$加上被乘数,即$ACC+=X$,若当前位是$0$,则$ACC$加上$0$(保持不变,跳过)。
3. 将$ACC$和$MQ$的数据连接在一起,全部逻辑右移一位,$ACC$数据高位补0$ACC$最后一个低位移到$MQ$的最高位。将$MQ$的最后一位抛弃。若是第i轮逻辑右移则$MQ$的前$i$位是结果的后$i$个低位值。
4. 从步骤二开始重复,字长若为$n+1$位,则重复$n$次,直到$MQ$的最后一位是符号位,则停止计算。此时$ACC$的全部和$MQ$的前$n$位都是结果。
5. 定点小数的小数位隐藏在符号位后面第一位,定点正数的小数位隐藏在$MQ$符号位的前一位
6. 将两个符号位的异或结果赋值给积最高位
+ 补码一位乘法:
+ 对于补码的乘法运算的逻辑也跟原码的类似,补码的计算就是使用$Booth$算法实现
+ 辅助位其实就是在$MQ$最后再加上一位,辅助位初始为$0$。每次右移会使$MQ$的最低位顶替原本的辅助位(事实上$MQ$共$n+2$位)
+ 为了保证统一,所以$ACC$和$X$都会增加一位,变成$n+2$位,多出来的一位就可以实现双符号位补码运算,而$MQ$还是用原理的单符号位。
+ 为了加快运算会有辅助电路实现$(-x)$的补码的运算。
+ 最后一次不需要移位直接根据辅助位和$MQ$最后一位判断进行相加。从而让乘数的符号位也参数运算中来确定最后结果的符号
&nbsp;|原码|补码
:----:|:--:|:--:
@@ -411,14 +389,6 @@ k|2|3|4|5|6|7
移位类型|逻辑右移|算术右移
符号位|不参与运算|参与运算
辅助位其实就是在MQ最后再加上一位辅助位初始为0。每次右移会使MQ的最低位顶替原本的辅助位事实上MQ共n+2位
为了保证统一所以ACC和X都会增加一位变成n+2位多出来的一位就可以实现双符号位补码运算二MQ还是用原理的单符号位。
为了加快运算会有辅助电路实现(-x)的补码的运算。
最后一次不需要移位直接根据辅助位和MQ最后一位判断进行相加。从而让乘数的符号位也参数运算中来确定最后结果的符号。
#### 定点数除法运算
进行除法操作时都是为了找到一位能让商乘除数能最大即余数最小但大于0的值。若除数被除数都是小数可以同时乘一个数变成整数再运算。