WBlog

哀吾生之须臾,羡长江之无穷

0%

计算机组成原理第二次作业

2.1 解释下列名词

真值

二进制数与十进制数一样有正负之分。书写时可以用“+”和“_”来表示数据的符号,这种数据书写格式也称为真值。

机器码

由于数据只有正、负两种符号,因此在计算机中很自然就采用进制的0和1来表示数据的符号,由符号和数值一起编码表示的二进制数称为机器数或机器码。

补码

在计算机科学中,补码是一种用于表示整数的二进制编码方式。它的特点是可以使用相同的加法运算处理加法和减法,这使得硬件设计更简单。

补码的定义如下:

  • 对于正数,补码与原码相同(原码就是正常的二进制表示)。

  • 对于负数,补码是其绝对值的二进制表示取反(即0变为1,1变为0,这也被称为按位取反或者求补),然后在结果上加1。

例如,假设我们有一个8位的二进制数,我们来看看-1的补码是如何计算的:

  1. 首先,我们取1的二进制表示(即00000001)。
  2. 然后,我们取反,得到11111110。
  3. 最后,我们在结果上加1,得到11111111。

所以,-1的8位补码表示是11111111。

这种方法的一个优点是没有所谓的“-0”。在一些其他的编码方案中,+0和-0是不同的,但在补码中,它们都被编码为00000000。

定点数

定点数表示法约定计算机中所有数据的小数点位置固定,其中,将小数点的位置固定在数据的最高数位之前(或符号位之后)的数据表示称为定点小数,而将小数点固定在最低数位之后的数据表示称为定点整数。另外,由于小数点位置固定,因此小数点不必再用符号表示,其位置也无须存储。

  1. 定点整数

设定点小数 x=x0x1x2…xn,则其在计算机中的表示形式如下图所示。符号位x用来表示数的正负,小数点的位置是固定的,在计算机中并不用去表示它。x~x是数值的有效部分,也称为尾数;x为最高有效位。在计算机中定点小数主要用于表示浮点数的尾数,并没有高级语言数据类型与之相对应。

image-20240319081921342

  1. 定点小数

设定点整数x=x0x1x2…xn,则其在计算机中的表示形式如图2.4所示。在C语言中char、short、int、long型都属于定点整数。
定点数能表示的数据范围与下列因素有关。
(1)机器字长。字长越长,其表示的数据范围就越大。
(2)所采用的机器数表示方法。通过前面对几种不同机器数的分析可知,补码和移码表示所能表示的数据范围比原码和反码所能表示的数据范围要多一个数。

  1. 定点数表示范围

定点数表示范围中的每一个数都可以对应数轴上的一个刻度,刻度在数轴上是均匀分布的,对于定点整数,最小刻度间距是1,而定点小数则为2"。当数据超出计算机所能表示的数据范围时称为溢出。当数据大于最大正数时,发生正上溢;当数据小于最小负数时,发生负上溢,具体如下图所示。
而定点小数还存在精度的问题,所有不在数轴刻度上的纯小数都超出了定点小数所能表示的精度,无法表示,此时定点小数发生精度溢出,只能采用舍入的方式近似表示。

image-20240319082227375

浮点数

浮点数是一种用于表示实数(即包含小数部分的数)的计算机编码系统。它是一种科学计数法的形式,用于表示非常大或非常小的数,以及精确到小数点后几位的数。

浮点数由三部分组成:符号位、指数和尾数。在这种表示法中,一个数被表示为:

N=2E×M=2±e×(±0.m)(1)N = 2^E \times M = 2^{\pm e} \times (\pm0.m) \tag{1}

采用这种方法,二进制浮点数可表示成阶码E(Exponent)和尾数M(Mantissa)两部分,其中阶码E是定点整数,而尾数M是定点小数。阶码的位数决定数据表示的范围,阶码的位数越多,能表示的数据范围就越大,而阶码的值决定了小数点的位置;尾数的位数决定数据表示的精度。阶码长度相同时,分配给尾数的数位越多,数据表示的精度就越高。浮点数数据的一般格式如下图所示。注意阶码和尾数均可采用不同的机器码进行表示,对应的浮点数表示范围也会略有不同。

image-20240319082911150

溢出

显然当阶码为最大值,尾数为最大值时,浮点数为正数最大值;而当阶码为最小值,尾数为正数最小值时,浮点数为正数最小值,这个值也就是浮点数的最小精度。同理,当阶码为最大值尾数为最小负数时,浮点数为负数最小值;而当阶码为最小值,尾数为负数最大值时,浮点数为负数最大值。
浮点数有效扩大了数据表示范围,但受计算机字长限制,浮点数仍然存在溢出现象。下图所示为浮点数的有效表示范围,图中“负数区域”“正数区域”及“0”是可表示的数据区域。当浮点数绝对值超过正数最大值时发生上溢,左、右两侧分别对应正上溢和负上溢,分别表示正无穷和负无穷;当非0浮点数绝对值小于正数最小值时,发生下溢。若运算结果发生上溢浮点运算器件会显示溢出标志;若运算结果发生下溢,虽然此时数据不能被精确表示,但由于发生下溢时数的绝对值很小,可作为机器0处理。同样,当一个浮点数在正、负数区域中但并不在某个数轴刻度上时,也会出现精度溢出的问题,此时只能用近似数表示。

image-20240319083047726

浮点数采用阶码和尾数的方式表示,阶码每变化一个刻度,数据就变大一倍,所以浮点数在数轴上的刻度并不是均匀分布的,越往数轴的左右两端,刻度越稀疏,这就是浮点数密度变化。

浮点数规格化

同一浮点数如果采用图 2.6所示的浮点数格式,可能存在多种表示形式,如 0.01111×21010.01111 \times 2^{101}还可以表示成 0.11110×21000.11110 \times 2^{100}​。尾数小数点的位置不同,就会有不同的尾数和阶码组合,这将给浮点数的表示带来麻烦。为了使浮点数的表示形式唯一并进一步提高数据的表示精度,通常要求浮点数在数据表示时对尾数进行规格化处理。所谓规格化处理就是使得尾数真值最高有效位为1,也就是尾数的绝对值应大于或等于(0.1)2或(0.5)10
对于非规格化尾数,需要对其进行规格化操作,即根据具体形式通过将非规格化尾数进行算术左移或右移,并同步减少或增加阶码值的操作进行规格化,对应的规格化方法分别称为左移规格化和右移规格化。
除使尾数真值大于0.5外,还有一种规格化数也经常使用,就是使得尾数绝对值大于1、小于2.这种规格化数参考了十进制科学记数法的表示方法,任意一个十进制数N都可表示为:

N=10E×M(1M10)(2)N = 10^E \times M (1 \leq \lvert M \rvert \leq 10)\tag{2}

示成如下形式注意尾数绝对值应大
注意十进制科学记数法中尾数绝对值应该大于1、小于10。而任意一个二进制数N也可表示成如下形式

注意尾数绝对值应该大于1,小于2

N=2E×M=2±e×(±1.m)(3)N = 2^E \times M = 2^{\pm e} \times (\pm1.m) \tag{3}

大于0.5和大于1的规格化数在本质上没有太大的区别,二者尾数真值最高有效位都是1,在实际表示时可以采用与定点数小数点一样的表示策略,无须单独表示最高有效位上的1,需要进行数据运算时再恢复最高有效位的表示,被隐藏的这一位又称为隐藏位。

在数学中,"模"是一种测量或者比较整数的工具。给定两个整数a和b,我们说"a模b"(通常写作a mod b或者a % b)是a除以b后的余数。

例如,17模5等于2,因为17除以5等于3余2。

模运算有许多有用的性质。例如,它是周期性的(即a mod b的值在a增加b的整数倍时不变),并且满足分配律即(a + b) mod c = (a mod c + b mod c) mod c。

在计算机科学中,模运算常常用于将值限制在某个范围内,例如将数组索引限制在数组长度内,或者将角度限制在0到360度之间。

BCD码(Binary Code Decimal)

BCD码编码较为简单,用4位二进制数表示09这10个数,由于4位二进制数能表示16种不同状态,因此需要从中选取10种来表示十进制数0~9,选取的方法有多种,常见的有8421码、2421 码、余3码等。

  • 8421码是一种有权编码,即表示十进制数的4位二进制数码的每一位都有确定的权值。其从高位到低位的权值分别为8、4、2、1,故称为8421码。

    8421码编码简单,0000~ 1001分别表示十进制的09,这10个4位二进制数按权展开后相加的值正好分别对应0~9。其余6种编码为非法编码。

  • 2421码则是另外一种有权编码,其4位二进制数的权值从左到右依次是 2、4、2、1。需要关注的是 2421码不具有单值性,即从权值考虑,1011和0101都可表示十进制数5,为了避免不-致性,2421码规定不用0101~1010这6个编码。另外,2421码编码具有自补的特点,即各位取反后正好为该数对9的补码。

  • 余3码是通过对 8421码的每个数码加3形成的一种无权编码。

这3种编码在进行算术运算时都需要进行结果的校正,运算器相对比较复杂。

  • 对8421码实现算术运算时,如果两数之和大于10,则运算结果需要加6修正,并向高位进位;
  • -2421码运算对结果的修正问题比 8421码更为复杂;
  • 余3码进行加法时如不产生进位,则运算结果需要减 3:若产生进位,则将进位送入高位,当前位加3校正。

机内码

机内码,也被称为内码或者原码,是计算机系统内部使用的字符编码。它通常是为了满足特定系统或应用的需要而设计的,可能与其他系统或应用使用的编码不同。

在计算机中,所有的信息最终都是以二进制的形式存储的。机内码就是将字符(包括字母、数字、符号等)转换为二进制形式的规则。

例如,ASCII(美国标准信息交换码)就是一种广泛使用的机内码,它使用7位二进制数来表示128个不同的字符。在ASCII中,大写字母A的机内码是65(或者二进制的1000001),小写字母a的机内码是97(或者二进制的1100001)。

请注意,机内码通常是特定于某个系统或应用的,不同的系统或应用可能会使用不同的机内码。

字形码

字形码是一种用于表示字符形状(字形)的编码系统。在计算机字体中,每个字符都有一个唯一的字形码,用于指定该字符的视觉表示。

字形码通常与字符编码(如ASCII或Unicode)分开处理。字符编码只是字符的抽象表示,而字形码则是字符在屏幕或打印机上的具体表示。

例如,Unicode字符U+0061(小写字母'a')可能有多个不同的字形,取决于字体。在一种字体中,'a'可能被表示为一个带有尾部的圆圈,而在另一种字体中,'a'可能被表示为一个没有尾部的圆圈。尽管这两个字形看起来不同,但它们都表示同一个字符,并且都有同一个Unicode字符编码(U+0061)。然而,它们的字形码将是不同的,因为字形码是用来指定字符的具体视觉表示的。

字库

字库是一种计算机文件,它包含了一套字符或符号的图形表示。这些图形表示被称为字形。每个字形对应一个字符编码,这样计算机就可以根据字符编码找到对应的字形,并在屏幕或打印机上显示出来。

字库通常用于存储特定字体的所有字符。例如,一个字库可能包含所有的拉丁字母、数字、标点符号,以及其他特殊字符。字库也可以包含其他语言的字符,例如汉字、日文假名和片假名、阿拉伯字母等。

在计算机中,字库通常以字体文件的形式存在。常见的字体文件格式包括TrueType(.ttf)、OpenType(.otf)、PostScript Type 1等。这些文件不仅包含字形,还包含字符间距、行高、字体样式等信息。

码距

在信息编码中,两个编码对应二进制位不同的个数称为码距,又称海明距离

如10101和00110从第一位开始依次有第1位、第4位、第5位等3位不同,则码距为3。

一个有效编码集中,任意两个码字的最小码距称为该编码集的码距。校验码的目的就是扩大码距,从而通过编码规则来识别错误代码。码距越大,抗干扰能力、纠错能力越强,数据冗余越大,编码效率越低选择码距时应考虑信息出错概率、系统容错率以及硬件开销等因素。

校验码

校验码是一种用于检测数据传输或存储过程中是否发生错误的技术。它通常是通过对数据应用某种算法,然后生成一个简短的数字或字符序列(即校验码)来实现的。在数据接收或读取时,会再次计算校验码,并与原始的校验码进行比较,以检测是否有错误发生。

校验码的一种常见形式是奇偶校验,它通过计算数据中的位数来检测单个位错误。另一种常见的形式是循环冗余校验(CRC),以及海明码,它可以检测更复杂的错误。

请注意,虽然校验码可以检测错误,但它们通常不能纠正错误。对于需要错误纠正的应用,通常会使用更复杂的技术,如前向纠错或纠错码。

海明校验码

海明校验码是一种错误检测和纠正的方法,由理查德·海明在1950年代提出。它可以检测到多位错误,并且能够自动纠正单位错误。
设海明校验码Hn……H2H1共n位,包含原始信息Dk…D2D1共k位,称为(n,k)码,校验位分别是Pr…P2P1,包含r个偶校验组,n=k+r。每个原始数据位至少位于两个以上的校验组。每个校验组的r位检错信息构成一个检错码Gr…G2G1假定0值表示无错,其他值表示海明码1位错的出错位置,则检错码可指出 2r-1种一位错。为了能指出n位海明编码中的所有一位错,"n、k、r"间应满足如下关系:

n=k+r2r1(4)n = k + r\leq2^{r-1} \tag{4}

海明校验码广泛应用于计算机内存、数据传输等领域,用于提高数据的可靠性。

根据海明码规则本人编写如下代码生成海明码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
import math
def generate_parity_code(data, parity='even'):
# 计算数据中1的个数
count = data.count('1')

# 如果选择的是偶校验,并且1的个数是奇数,或者选择的是奇校验,并且1的个数是偶数,那么校验位为1
if (parity == 'even' and count % 2 != 0) or (parity == 'odd' and count % 2 == 0):
data += '1'
else:
data += '0'
return data
# 汉明码
def generate_hamming_code(k:int):
for i in range(1, 64):
if 2 ** (i - 1) >= k + i:
binary = i
break
i += 1
result = [] # list[str]
# 校验位
P = []
# 校验位位置
P_index = []
# 数据位
D = []
# 数据位位置
D_index = []
# 校验位的码距
D_cnt = {"D" + str(i):0 for i in range(1,k+1)}
# 校验码生成结果字典
P_result = {"P" + str(i):[] for i in range(1,binary + 1)}
# 定义海明码类,内容包括数据位,二进制形式,校验位计算,校验位组
class hamming_code:
def __init__(self, data,binary_format,bit_verification_calculation,check_bit_group):
self.data = data
self.binary_format = binary_format
self.bit_verification_calculation = bit_verification_calculation
self.check_bit_group = check_bit_group
def __str__(self) -> str:
return f"数据位:{self.data} 二进制形式:{self.binary_format} 校验位计算:{self.bit_verification_calculation} 校验位组:{self.check_bit_group}"
# 生成校验位
P = [("P" + str(i + 1)) for i in range(binary)]
# 生成数据位
D = [("D" + str(i + 1)) for i in range(k)]
j = 0
o = 0
# 生成海明码
for i in range(1, k + binary):
if i == 2 ** j:
result.append(P[j])
P_index.append(i)
j += 1
else:
result.append(D[o])
D_index.append(i)
o += 1
if j < len(P):
result.append(P[j])
P_index.append(len(result))
# 反转结果使得其便于查看
result.reverse()
f = open("汉明码.md", "a",encoding="UTF-8")
# 清除原有内容
f.truncate(0)
f.write("**汉明码:**")
for i in range(len(result)-2):
if result[i][0] == "P":
f.write("`" + result[i] + "`")
else:
f.write(result[i])
f.write("`P2P1`")
print("汉明码:", result)
print("数据位位置:", D_index)
print("校验位位置:", P_index)
# 再次反转适合处理
result.reverse()
hamming_code_result = []

# 生成海明码内容类的相关数据
# 数值最大值,保证码距相等
D_max = 0
for i in D_index:
# 获取反转后的二进制形式
data_binary = bin(i)[:1:-1]
tmp_calculate = []
check_bit_group = []
tmp_calculate.append(str(i) + " = ")
for t in range(len(data_binary)):
if data_binary[t] == '1':
# 获取二进制位的校验位计算
if len(tmp_calculate) == 1:
tmp_calculate.append(str(2 ** t))
else:
tmp_calculate.append(" + "+str(2 ** t))
P_result["P" + str(t + 1)].append(result[i-1])
check_bit_group.append(result[2 ** t - 1])
# 统计数据位出现次数
D_cnt[result[i-1]] += 1
D_max = max(D_max,D_cnt[result[i-1]])
tmp_hamming_code = hamming_code(result[i-1],data_binary[::-1],tmp_calculate,check_bit_group)
hamming_code_result.append(tmp_hamming_code)
# 输出汉明码结果类的相关信息
# 有数据位,二进制形式,校验位计算,校验位组
f.write("\n| 数据位 | 二进制形式 | 校验位计算 | 校验位组 |\n| :----: | :--------: | :--------: | :------: |")
for i in hamming_code_result:
print(i)
if(i.binary_format.count('1') < binary):
i.binary_format = "0" * (binary - len(i.binary_format) -1) + i.binary_format
f.write(f"\n| {i.data} | {i.binary_format} | {''.join(i.bit_verification_calculation)} | {','.join(i.check_bit_group)} |")
# 输出校验位的生成部分,确认是否正确
for i in P_result:
print(i, ":", P_result[i])
if P_result[result[-1]] == []:
for i in D_cnt.keys():
# 不符合最大码距的数据位添加到校验位的生成部分
if D_cnt[i] != D_max:
P_result[result[-1]].append(i)
P_result[result[-1]].sort()
# 输出latex的校验码
f.write("\n\n$$\n")
# 校验位的形成部分打印
for i in P_result:
f.write("\\\\")
print(i, ":", P_result[i])
P_result[i].sort(key = lambda x: int(x[1:]))
for l in range(len(P_result[i])):
if l != 0:
f.write(f"\\oplus {P_result[i][l]}")
else:
f.write(f"{i} = {P_result[i][l]}")
f.write("\n$$\n")
# 输出latex的校验码
f.write("# 校验\n")
f.write("\n$$\n")
for i in P_result:
f.write("\\\\")
print("S" + i[1:], ":", P_result[i])
for l in range(len(P_result[i])):
if l != 0:
f.write(f"\\oplus {P_result[i][l]}")
else:
f.write(f"S{i[1:]} = {i} \\oplus {P_result[i][l]}")
f.write("\n$$\n")
f.close()
generate_hamming_code(8)

修改generate_hamming_code(x)中的参数即可生成对应海明码的md文件

效果如下

image-20240319090631229

CRC码

循环几余校验(Cyclic Redundancy Check,CRC)是一种基于模2运算建立编码规则的校验码,在磁存储和计算机通信方面应用广泛。

设CRC码长度共n位,其中原始数据信息为Ck-1Ck-2…C1C0共k位,校验位Pr-1Pr-2…P0共r位,称为(n,k)码,则CRC码为Ck-1Ck-2……C0Pr-1Pr-2……P0。和海明码一样,CRC码也需要满足如下关系式:

n=k+r2r1(5)n = k + r\leq2^{r-1} \tag{5}

根据CRC码编码规则本人写出如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# CRC码
def calculate_crc(data, divisor):
# 获取移位位数
shift_bits = len(divisor) - divisor.find('1') -1
# 将数据和除数转换为列表,以便进行操作
data = list(map(int, data))
data = data + [0] * (len(divisor) - 1)
# 保存原始数据,以便最后生成CRC码
original_data = data.copy()
divisor = list(map(int, divisor))

# 执行除法操作
for i in range(len(data) - len(divisor) + 1):
if data[i] == 1:
for j in range(len(divisor)):
data[i+j] ^= divisor[j]
# 生成CRC码
crc_code = ''.join(map(str, data[-(len(divisor)-1):]))
for i in range(len(crc_code)):
j = int(crc_code[i])
if j == 1:
original_data[len(data) - (len(crc_code) - i)] = 1
print(*original_data)
print("CRC码:", crc_code)
return original_data
print(*calculate_crc('1100', '1011')) #输出1 1 0 0 0 1 0

2.2 选择题

  1. 可以组成10000011,原码是10000011即-125,选B

  2. 无符号65535二进制形式为1111111111111111,若转换为带符号数则为变为-1,选A

  3. short表明应该是八位,65530对应十六进制表示0000 FFFAH,选B

  4. short类型的-32767转换为二进制为1000000000000001,对应unsigned short32769,选D

  5. 在IEEE 754标准中,一个浮点数被表示为三部分:符号位、指数位和尾数位。

    对于单精度(32位)浮点数:

    • 符号位:1位,表示正负,0为正,1为负。
    • 指数位:8位,偏移量为127。
    • 尾数位:23位,表示小数部分。

    对于-8.25

    • 符号位:因为是负数,所以符号位为1。
    • 指数位:8的二进制表示为1000,所以指数为3。加上偏移量127,得到130,即10000010
    • 尾数位:0.25的二进制表示为01,规格化后尾数为00001000000000000000000000

    所以,-8.25的IEEE 754单精度浮点数表示为:'1 10000010 000010000000000000000000'

    转换为16进制即为: C1040000

    A

  6. C6400000H二进制表示为11000110010000000000000000000000

  • 符号位: 为1,所以是负数

  • 指数位: 10001100减去偏移量127得到十进制为13

  • 尾数位: 10000000000000000000000,得到0.5

    规格化后得1.5×213-1.5 \times 2^{13}

    A

  1. IEEE754单精度浮点格式表示的最大整数二进制形式为01111111111111111111111111111111

    换算过来是2127×(2223)2^{127} \times (2 - 2^{-23})

    212821042^{128} - 2^{104}

    D

  2. 取如下值

  • 符号位: 0

  • 指数位: 1

  • 尾数位: 0

    此时可以得到规格化最小值2-126

    A

  1. x转换为二进制形式为11001100100100000000000000000000,对应规格化浮点数为1.125×225-1.125 \times 2^{25}
    y转换为二进制形式为10110000110000000000000000000000,对应规格化浮点数为1.5×230-1.5\times 2^{-30}

    可以看出,且二者符号位相同

    A

  2. Ⅰ中的i为整数,转换为浮点数后再转整数并未有精度损失,所以Ⅰ是对的

​ Ⅱ中的f为单精度浮点数,转换为整数后再转为浮点数出现了精度损失,所以是错的

​ Ⅲ中的f为单精度浮点数,转换为双精度浮点数并未有精度损失,所以Ⅲ是对的

​ Ⅳ因为在将单精度数提升为双精度数进行计算时,可能会引入一些舍入误差,所以Ⅳ错

​ 选B

  1. 根据公式k+r2r1k + r \leq 2^r - 1

​ 带入8可得到r最小值为4

​ 选C

2.3 回答下列问题

  1. 为什么计算机中采用二进制进行数据表示和运算

计算机使用二进制进行数据表示和运算的主要原因是硬件设计的简单性。在最基本的电子级别,一个开关只有两种状态:开(通常表示为1)和关(通常表示为0)。这非常适合于二进制系统,其中只有两个可能的值:0和1。

此外,二进制也使得逻辑运算(如AND、OR和NOT)变得简单直接,这些运算在计算机程序中非常常见。二进制还使得错误检测和纠正变得更容易,因为只需要检查和修正一个位,而不是一个复杂的十进制数字。

最后,虽然对于人类来说,十进制系统可能更直观,但是计算机处理二进制数据的速度要比处理十进制数据快得多。这是因为二进制运算可以直接通过硬件电路实现,而不需要复杂的算法。

  1. 为什么计算机采用补码表示带符号整数

计算机采用补码来表示带符号整数的主要原因有以下几点:

​ 1. 统一运算规则:在补码表示法中,加法和减法可以使用相同的硬件电路进行运算,无需区分正数和负数,简化了硬件设计。

​ 2. 避免出现多种零:在原码和反码表示法中,+0和-0是两种不同的表示,而在补码表示法中,只有一种零,避免了这种混淆。

​ 3. 扩大了负数的表示范围:在补码表示法中,可以表示的负数比正数多一个,例如在8位二进制数中,可以表示的最大正数是127,而最小的负数是-128。

​ 4. 方便进行逻辑运算:补码的形式更适合进行逻辑运算,如位移、与、或、非等操作。

因此,现代计算机系统普遍采用补码来表示带符号整数。

  1. 浮点数表示范围和精度分别由什么决定

​ 浮点数的表示范围和精度主要由以下三个部分决定:

​ 1. 符号位(Sign bit):这一位决定了浮点数的正负。

​ 2. 指数位(Exponent):这部分决定了浮点数的大小范围。在IEEE 754标准中,单精度浮点数的指数部分有8位,双精度浮点数的指数部分有11位。

​ 3. 尾数位(Mantissa/Fraction):这部分决定了浮点数的精度。在IEEE 754标准中,单精度浮点数的尾数部分有23位,双精度浮点数的尾数部分有52位。

指数位和尾数位的长度决定了浮点数的表示范围和精度。指数位的长度决定了浮点数的表示范围,尾数位的长度决定了浮点数的精度。更长的指数位可以表示更大范围的数,更长的尾数位可以表示更高精度的数。

  1. 汉字输入码,机内码和字形码在汉字处理过程中有何作用

在计算机处理汉字的过程中,汉字输入码、机内码和字形码各有其特定的作用:

​ 1. 汉字输入码:这是用户在键盘上输入汉字时使用的编码。例如,拼音输入法中,“你好”的输入码就是"nihao"。输入码的设计目标是方便用户输入。

​ 2. 机内码:这是计算机内部存储和处理汉字时使用的编码。例如,GB2312、GBK和UTF-8都是机内码。机内码的设计目标是高效地存储和处理汉字。

​ 3. 字形码:这是计算机在显示汉字时使用的编码。字形码通常是一个点阵或矢量图形,描述了汉字的形状。字形码的设计目标是清晰、美观地显示汉字。

在汉字从输入到显示的过程中,会经历从输入码到机内码,再到字形码的转换。这个过程涉及到输入法、字符编码和字体渲染等多个技术。

  1. 在机内码如何区分ASCII字符和一个汉字字符

在计算机内部,ASCII字符和汉字字符的区分主要依赖于所使用的字符编码方案。

例如,在UTF-8编码中,ASCII字符只占用一个字节,而汉字通常占用三个字节。UTF-8编码的一个重要特性是它是可变长度的,也就是说,不同的字符可能占用不同数量的字节。这使得UTF-8编码可以兼容ASCII编码,并且可以表示更多的字符。

在GB2312、GBK或GB18030等编码中,ASCII字符占用一个字节,汉字占用两个字节或更多。这些编码方案都是专门为汉字设计的,可以表示大量的汉字。

在处理字符串时,计算机会根据当前的字符编码方案来解析每个字符占用的字节数,从而区分ASCII字符和汉字字符。

  1. 如何识别浮点数的正负?浮点数能表示的数值范围和数值的精确度取决于什么

    在IEEE 754标准中,浮点数的最高位(称为符号位)用于表示浮点数的正负。如果符号位是0,那么这个浮点数就是正数;如果符号位是1,那么这个浮点数就是负数。

    浮点数能表示的数值范围和数值的精确度主要取决于两个部分:指数部分和尾数部分。

    1. 指数部分:决定了浮点数的大小范围。指数部分的位数越多,能表示的数的范围就越大。
    2. 尾数部分:决定了浮点数的精度。尾数部分的位数越多,能表示的数的精度就越高。

    例如,在IEEE 754标准的单精度浮点数中,符号位占1位,指数部分占8位,尾数部分占23位。在双精度浮点数中,符号位占1位,指数部分占11位,尾数部分占52位。

  2. 简述CRC校验码的纠错原理

CRC(Cyclic Redundancy Check,循环冗余校验)是一种常用的数据校验方法,它的主要原理是通过在数据后面添加一些冗余位(即CRC校验码),使得整个数据(包括原始数据和冗余位)能被一个预定的多项式整除。

CRC校验的过程如下:

  1. 在发送端,发送者首先选择一个多项式,并将其转换为二进制形式。然后,发送者将原始数据扩展(通常是在数据的后面添加若干个0),使得扩展后的数据长度能被多项式的长度整除。接着,发送者将扩展后的数据与多项式进行模2除法运算(即异或运算),得到的余数就是CRC校验码。最后,发送者将CRC校验码添加到原始数据的后面,一起发送出去。
  2. 在接收端,接收者收到数据后,也用同样的多项式对收到的数据(包括原始数据和CRC校验码)进行模2除法运算。如果余数为0,那么接收者就认为数据没有错误;如果余数不为0,那么接收者就认为数据有错误。

需要注意的是,CRC只能检测出错误,但不能纠正错误。如果需要纠错功能,就需要使用其他的纠错码,如海明码(Hamming code)等。

2.4 已知数的补码表示形式,求数的真值

[x][x]_{补} 真值
0.10010 0.10010
1.10010 -0.01110
1.11111 -0.00001
1.00000 -1.00000
0.10001 0.10001
1.00001 -0.11111

2.5 分析下列几种情况下所能表示的数据范围分别是多少

类型 数据范围
16位无符号数 02161[0,65535]0\sim2^{16}-1 即[0,65535]
16位原码定点小数 1.1111111111111110.111111111111111(1215)12151.111111111111111 \sim 0.111111111111111即-(1-2^{-15}) \sim 1-2^{-15}
16位补码定点小数 1.0000000000000000.111111111111111112151.000000000000000 \sim 0.111111111111111即-1\sim 1-2^{-15}
16位补码定点整数 1000000000000000011111111111111121521511000000000000000 \sim 0111111111111111即-2^{15}\sim 2^{15}-1

2.6 用IEEE754 32位单精度浮点数标准表示下列十进制数

(1) -6.625

-6.625转化为二进制为110.1011.10101×22-110.101转化为科学计数法-1.10101 \times 2^2

得到2进制指数位为2,尾数为10101000000000000000000

1 10000001 10101000000000000000000

  • 符号位: 1,为负数
  • 指数位: 2 + 127 = 129 ,即10000001
  • 尾数位: 10101000000000000000000
  • 结果: 1 10000001 10101000000000000000000 = (C0D40000)16

(2) 3.1415927

3.1415927转化为二进制为11.001001000011111101101010100011.100100100001111110110101010001×2111.00100100001111110110101010001转化为科学计数法1.100100100001111110110101010001 \times 2^1

得到2进制指数位为1,尾数为100100001111110110101010001

  • 符号位: 0,为正数
  • 指数位: 1 + 127 = 128 ,即10000000
  • 尾数位: 10010010000111111011010
  • 结果: 0 10000000 10010010000111111011010 = (40490FDB)16

(3) 64000

64000转化为二进制为11111010000000001.111101000000000×2151111101000000000转化为科学计数法1.111101000000000 \times 2^{15}

得到2进制指数位为1,尾数为100100001111110110101010001

  • 符号位: 0,为正数
  • 指数位: 15 + 127 = 142 ,即10001110
  • 尾数位: 11110100000000000000000
  • 结果: 0 10001110 11110100000000000000000 = (477A0000)16

2.7 求与单精度浮点数43940000H对应的十进制数

43940000H对应的二进制编码为0 10000111 00101000000000000000000

  • 符号位: 0,为正数
  • 指数位: 10000011,即135,减去偏移量即为8
  • 尾数: 00101000000000000000000

那么十进制数也就是1.00101000000000000000000×28=100101000=2961.00101000000000000000000 \times 2^8 = 100101000 = 296

2.8 解释下列名词

变形补码

补码是一种用于表示有符号整数的二进制数系统。在补码系统中,负数是通过取其绝对值的二进制表示,然后反转所有的位(即取反,0变为1,1变为0),最后加1来得到的。

变形补码是一种特殊的补码形式,它用于表示浮点数的指数部分。在IEEE 754浮点数标准中,指数部分使用了一种称为偏移二进制(offset binary)或者变形补码(bias representation)的表示方法。

在单精度浮点数中,指数部分是8位,偏移量是127。也就是说,实际的指数值等于存储的指数值减去127。例如,如果存储的指数值是10000001(二进制),那么实际的指数值就是1(因为10000001等于129,129减去127等于2)。

在双精度浮点数中,指数部分是11位,偏移量是1023。

这种表示方法的好处是,它可以很容易地表示出正数、负数和零,而不需要额外的符号位。

溢出

溢出(Overflow)是一种常见的错误情况,发生在当一个运算的结果超出了可表示或存储的范围时。例如,当你试图将一个大于127的整数存储在一个8位的有符号整数中时,就会发生溢出。

溢出可以分为两种类型:整数溢出和浮点数溢出。

  1. 整数溢出:当一个整数的值超过了其数据类型所能表示的最大值或最小值时,就会发生整数溢出。例如,一个8位的有符号整数可以表示的最大值是127,最小值是-128。如果一个运算的结果超过了这个范围,就会发生整数溢出。
  2. 浮点数溢出:当一个浮点数的值超过了其数据类型所能表示的最大值时,就会发生浮点数溢出。例如,一个单精度浮点数可以表示的最大值大约是3.4E+38,如果一个运算的结果超过了这个值,就会发生浮点数溢出。

在大多数编程语言中,溢出通常会导致程序错误,或者产生不可预期的结果。因此,编写程序时需要注意避免溢出的情况。

阵列乘法器

运算速度的提高对机器性能的提高至关重要。原码、补码一位乘法主要是通过加法器的循环累加计算多个位积和求解乘积的,速度较慢。为提高多个位积求和的速度,可以采用硬件的方式实现阵列乘法器。其基本思想是采用类似手动乘法运算的方法,用大量与门阵列同时产生手动乘法中的各乘积项,同时将大量一位全加器按照手动乘法运算的需要构成全加器阵列。图3.13所示为一个4位乘4位无符号阵列乘法器的工作原理图。

恢复余数除法

在原码恢复余数法中,比较被除数(余数)与除数的大小是用减法实现的。对原码除法而言操作数以绝对值的形式参与运算。因此,相减结果为正(符号位为0)说明够减,商上1;相减结果为负(符号位为1)说明不够减,商上0。
由于除法通过减法实现,当商上1时,减法得到的差值就是余数,可以继续进行后续的除法操作。但商上0时表明不够减,减法得到的余数是负数,因此需要将余数加上除数,即将余数恢复成比较操作之前的数值,这种方法就称为恢复余数法。

不恢复余数除法

不恢复余数法是对恢复余数法的改进,主要特点是不够减时不需要恢复余数,而根据余数符号进行不同的运算处理。其运算步数固定,控制简单,有效提高了除法运算速度。

并行进位

在基本的二进制加法器中,每一位的加法运算都依赖于低一位的进位。这就意味着,加法运算必须从最低位开始,然后逐位向上进行,每一位的运算都必须等待前一位的运算完成。这种加法器被称为串行进位加法器。

并行进位加法器通过预先计算所有可能的进位,从而避免了等待前一位运算完成的延迟。这就使得所有位的加法运算可以同时进行,大大提高了加法运算的速度。

并行进位加法器的设计和实现比串行进位加法器要复杂得多,但是在需要进行大量加法运算的应用中,它的高速性能使得这种复杂性是值得的。

先行进位

与并行进位相同

算术移位

算术移位是一种位操作,用于将一个二进制数的所有位向左或向右移动一定的位数,同时保持该数的符号不变。

算术右移(Arithmetic Right Shift):在算术右移中,最左边的位(通常是符号位)被复制到新的最左边的位,而其他位则向右移动一位。这种操作相当于将原数除以2。

算术左移(Arithmetic Left Shift):在算术左移中,最右边的位被丢弃,最左边的位(符号位)保持不变,而其他位则向左移动一位。这种操作相当于将原数乘以2。

需要注意的是,算术移位和逻辑移位是不同的。在算术移位中,空出的位总是被填充为符号位。

逻辑移位

逻辑移位是一种位操作,用于将一个二进制数的所有位向左或向右移动一定的位数。

逻辑右移(Logical Right Shift):在逻辑右移中,最左边的位被填充为0,而其他位则向右移动一位。这种操作相当于将原数除以2。

逻辑左移(Logical Left Shift):在逻辑左移中,最右边的位被丢弃,最左边的位被填充为0,而其他位则向左移动一位。这种操作相当于将原数乘以2。

需要注意的是,逻辑移位和算术移位是不同的。在逻辑移位中,空出的位被填充为0。

对阶

阶码和尾数均采用补码有利于采用前面介绍的运算方法进行运算,设有两个浮点数:

X=2m×Mx    Y=2n×MyX = 2^m \times M_x~~~~Y = 2^n\times M_y

当m=n时,尾数部分直接运算即可得到浮点形式的运算结果。但当参加运算的两个数的阶码m与n并不相等时,必须先设法让两个阶码相等后才能进行尾数部分的运算。使阶码相等的过程称为对阶,对阶完成后即可进行尾数的加减法运算

规格化

在计算机科学中,规格化(Normalization)是一种处理数据的方法,通常用于浮点数的表示和计算。

在IEEE 754浮点数标准中,规格化是指将浮点数表示为1.xxxx的形式,其中1是显式的,xxxx是尾数部分。例如,二进制数1011.1101规格化后就变成了1.0111101 * 2^3。

规格化的主要目的是使得浮点数的表示更加精确。因为在规格化的浮点数中,尾数部分总是以1开头,所以可以最大限度地利用尾数的位数,从而提高浮点数的精度。

2.9 选择题

  1. r1真值为-2,r2真值为-14,r3真值为-112,r4真值为-8,八位补码数据表示范围从-128~127,可以算出B的答案溢出,其他未溢出,选B

  2. x真值为-12,y真值为-80,z=-64那么选A

  3. x真值为-33,y真值为65,x-y为-98转为机器码为FFFF FF9EH,选A

  4. 逻辑移位添加0而不动符号位,算术移位需添加符号位,移位后结果如下01101100,11101100,选B

  5. x阶码可记为00,111,尾数可记为00,11101

    y阶码可记为00,101,尾数可记为00,10100

    首先对阶,y阶码向x看齐,y阶码加2变为00,111

    尾数相加得01,00010,符号位为01,进行右规,阶码+1

    得X+Y阶码为01,000,X+Y尾数位00,10001

    阶码为01说明发生溢出

    D

    2.10 回答下列问题

    1. 为什么采用并行进位能提高加法器运算速度

      并行进位(Carry-Lookahead)加法器能提高加法器运算速度的原因在于它减少了进位的等待时间。

      在传统的串行进位加法器中,每一位的加法运算都依赖于前一位的进位结果。这就意味着,加法运算必须从最低位开始,然后逐位向上进行,每一位的运算都必须等待前一位的运算完成。这种依赖关系导致了加法运算的延迟,特别是在处理多位数时。

      而并行进位加法器通过预先计算所有可能的进位,从而避免了等待前一位运算完成的延迟。这就使得所有位的加法运算可以同时进行,大大提高了加法运算的速度。

    2. 如何判断浮点数运算结果是否为规格化数?如果不是规格化数字,如何进行规格化

      在IEEE 754浮点数标准中,一个浮点数被规格化是指它的表示形式为1.xxxx * 2^n,其中1是显式的,xxxx是尾数部分,n是指数部分。如果浮点数的表示形式不是这样,那么它就不是规格化的。

      判断一个浮点数是否规格化,可以通过检查它的二进制表示。如果它的二进制表示的最高位(除了符号位)是1,那么它就是规格化的。否则,它就不是规格化的。

      如果一个浮点数不是规格化的,可以通过以下步骤进行规格化:

      1. 如果浮点数的二进制表示的最高位(除了符号位)是0,那么将它向左移动,直到最高位变为1。同时,每移动一位,就将指数部分减1。
      2. 如果浮点数的二进制表示的最高位(除了符号位)是1,但是它的小数部分有多余的0,那么将它向右移动,直到小数部分的最后一位变为1。同时,每移动一位,就将指数部分加1。

    2.11 已知x和y,用变形补码计算x+y,并判断结果是否溢出

    1. x = 0.11010,y = 0.101110
      [x+y]=01.10001[x + y]_补 = 01.10001
      正溢出
    2. x = 0.10111,y = -0.10100
      [x+y]=00.01001[x + y]_补 = 00.01001
      未溢出
    3. x = -0.10111,y=-0.11000
      [x+y]=10.10001[x+y]_补 = 10.10001
      负溢出

2.12 已知x和y,用变形补码计算x-y,并判断结果是否溢出

  1. x = 0.11011,y = 0.11101

    [xy]=11.11110[x - y]_补 = 11.11110

    未溢出

  2. x = 0.10111,y=0.11110

    [xy]=11.11001[x - y]_补 = 11.11001

    未溢出

  3. x = -0.11111,y=-0.11001

    [xy]=11.11010[x-y]_补 = 11.11010

    未溢出

2.13 用原码一位乘法计算x * y = ?

  1. x=0.11111   y=0.11101x = 0.11111~~~y = 0.11101

    部分积 乘数 说明
    00.00000
    + 00.11111
    11101 部分积 初态z0 = 0
    +|X|
    00.11111
    -> 00.01111
    + 00.00000

    11110
    -> 1 得z1
    + 0
    00.01111
    -> 00.00111
    + 00.11111

    11111
    -> 1 得z2
    + |X|
    01.00110
    -> 00.10011
    + 00.11111

    01111
    -> 1 得z3
    +|X|
    01.10010
    -> 00.11001
    + 00.11111

    00111
    -> 1 得z4
    +|X|
    01.11000
    -> 00.11100

    00011
    -> 1 得z5

    符号位 = XsYs=10=1X_s \oplus Y_s = 1 \oplus 0 = 1

    结果为 -0.1110000011

  2. x=0.11010   y=0.01011x = - 0.11010~~~y = - 0.01011

    部分积 乘数 说明
    00.00000
    + 00.11010
    01011 部分积 初态z0 = 0
    +|X|
    00.11010
    -> 00.01101
    + 00.11010

    00101
    -> 1 得z1
    + |X|
    01.00111
    -> 00.10011
    + 00.00000

    10010
    -> 1 得z2
    + 0
    00.10011
    -> 00.01001
    + 00.11010

    11001
    -> 1 得z3
    +|X|
    01.00011
    -> 00.10001
    + 00.00000

    11100
    -> 1 得z4
    +0
    00.10001
    -> 00.01000

    11110
    -> 1 得z5

    符号位 = XsYs=11=0X_s \oplus Y_s = 1 \oplus 1 = 0

    结果为0.0100011110

2.14 用补码一位乘法计算 x * y = ?

x=0.10110   y=0.00011[X]=0.10110   [Y]=1.11101   [X]=1.01010 x=0.10110~~~y=-0.00011\\ [X]_补 = 0.10110~~~[Y]_补=1.11101~~~[-X]_补=1.01010

部分积 乘数 说明
00.00000
+ 01.01010

1.111010
yn+1yn=1+[X]y_{n+1}-y_n=-1\\+[-X]_补
右移一位
01.01010
-> 00.10101
+ 00.10110

0111101
yn+1yn=1+[X]y_{n+1}-y_n=1\\+[X]_补
右移一位
01.01011
-> 00.10101
+ 11.01010

1011110
yn+1yn=1+[X]y_{n+1}-y_n=-1\\+[-X]_补
右移一位
11.01111
-> 11.10111
+ 00.00000

1101111
yn+1yn=0+0y_{n+1}-y_n=0\\+0
右移一位
11.10111
-> 11.11011
+ 00.00000

1110111
yn+1yn=0+0y_{n+1}-y_n=0\\+0
右移一位
11.11011
-> 11.11101
+ 00.00000

1111011
yn+1yn=0+0y_{n+1}-y_n=0\\+0
最后一位数据不移位
11.11101 11110
[x×y]=1.1110111110[x \times y]_补 = 1.1110111110

x=0.011010   y=0.011101[X]=1.100110   [Y]=1.100011   [X]=0.011010 x=-0.011010~~~y=-0.011101\\ [X]_补 = 1.100110~~~[Y]_补=1.100011~~~[-X]_补=0.011010

部分积 乘数 说明
00.000000
+ 00.011010

1.1000110
yn+1yn=0+[X]y_{n+1}-y_n=0\\+[-X]_补
右移一位
00.011010
-> 00.001101
+ 00.000000

01100011
yn+1yn=1+0y_{n+1}-y_n=1\\+0
右移一位
00.001101
-> 00.000110
+ 11.100110

10110001
yn+1yn=0+[X]y_{n+1}-y_n=0\\+[X]_补
右移一位
11.101100
-> 11.110110
+ 00.000000

01011000
yn+1yn=0+0y_{n+1}-y_n=0\\+0
右移一位
11.110110
-> 11.111011
+ 00.000000

00101100
yn+1yn=1+0y_{n+1}-y_n=-1\\+0
右移一位
11.111011
-> 11.111101
+ 00.011010

10010110
yn+1yn=1+[X]y_{n+1}-y_n=-1\\+[-X]_补
右移一位
00.010111
-> 00.001011
+ 00.000000

11001011
yn+1yn=0+0y_{n+1}-y_n=0\\+0
最后一位不移位
-> 00.001011
110010
[x×y]=0.001011110010[x \times y]_补 = 0.001011110010