# 数值数据的表示

# 数的机器码表示

# 原码

真值 00 有两种不同的表示

无法直接进行运算

定点小数 xx 的原码形式

[x]={x0x<11+x1<x0[x]_原=\left\{\begin{matrix} x & 0\leq x<1\\ 1+|x| & -1<x\leq 0 \end{matrix}\right.

定点整数 xx 的原码形式

[x]={x0x<2n2n+x2n<x0[x]_原=\left\{\begin{matrix} x & 0\leq x<2^n\\ 2^n+|x| & -2^n<x\leq 0 \end{matrix}\right.

# 反码

真值 00 有两种不同的表示

可以直接进行运算,但是需要采用循环进位的方式

也称为 11 的补码,符号位和原码相同

  • 真值为正数,反码和原码相同
  • 真值为负数,反码数值位为真值数值位取反

根据反码编码规则可知

⚠️当真值 xx 是负数时 [x]+x[x]_反+|x| 的值为11 编码

  • 11 编码在定点小数中的值为

    22n2-2^{-n}

  • 11 编码在定点整数中的值为

    2n+112^{n+1}-1

定点小数 xx 的反码形式

[x]={x0x<1(22n)+x1<x0[x]_反=\left\{\begin{matrix} x & 0\leq x<1\\ (2-2^{-n})+x & -1<x\leq 0 \end{matrix}\right.

定点整数 xx 的反码形式

[x]={x0x<2n(2n+11)+x2n<x0[x]_反=\left\{\begin{matrix} x & 0\leq x<2^n\\ (2^{n+1}-1)+x & -2^n<x\leq 0 \end{matrix}\right.

# 补码

00 的表示唯一

多出一个编码状态,在定点小数中是 1-1,在定点整数中是 2n-2^n

计算机中二进制有字长限制,数据最高位进位的位权值就是模数,计算机二进制数据的运算会将超过模数的部分自动舍去,属于典型的有模运算

定点小数 xx 的补码形式

因为模为最高位进位的权值,所以模为 22

[x]={x0x<12+x1<x0(mod2)[x]_{补}=\left\{\begin{matrix} x & 0\leq x<1\\ 2+x & -1<x\leq 0 \end{matrix}\right.\pmod 2

定点整数 xx 的补码形式

共有 n+1n+1 位,模数为 2n+1{2^{n+1}}

[x]={x0x<2n2n+1+x2n<x0(mod2n+1)[x]_{补}=\left\{\begin{matrix} x & 0\leq x<2^n\\ 2^{n+1}+x & -2^n<x\leq 0 \end{matrix}\right.\pmod{2^{n+1}}

# 求补码的方法

已知原码,求补码

符号位不变,对于数据位:

  1. 反码法:对真值 取反 + 1
  2. 扫描法:对真值从右往左扫描,右边起第一个 11 及其右边的 00 保持不变,其余各位求反

已知补码,求相反数补码

  • 连符号位一起取反,末尾加 11

# 变形补码

用两个二进制位表述数据的符号,其余与补码相同

  • 0000 表示正数,1111 表示负数
  • ❗️0101 表示运算出现正溢出,1010 表示运算出现负溢出

定点小数 xx 的变形补码形式

模为 44

[x]={x0x<14+x1<x0(mod4)[x]_{补}=\left\{\begin{matrix} x & 0\leq x<1\\ 4+x & -1<x\leq 0 \end{matrix}\right.\pmod 4

定点整数 xx 的补码形式

共有 n+2n+2 位,模数为 2n+2{2^{n+2}}

[x]={x0x<2n2n+2+x2n<x0(mod2n+2)[x]_{补}=\left\{\begin{matrix} x & 0\leq x<2^n\\ 2^{n+2}+x & -2^n<x\leq 0 \end{matrix}\right.\pmod{2^{n+2}}

# 移码

00 的表示唯一,1000...01000...0

只用于定点整数的表示,常用于浮点数的阶码

[x]=x+2n,2nx<2n[x]_{移}=x+2^n ,\, -2^n\leq x<2^n

对比可知

[x]=[x]+2n(mod2n+1)[x]_补=[x]_移+2^n\pmod {2^{n+1}}

  1. 移码和补码的符号位相反,数值位相同
  2. 移码的符号位 00 表示负数, 11 表示正数
  3. 移码中的 00 表示唯一,为 1000...01000...0

# 四种机器码的总结

负数的反码和补码比大小:

比较数值部分,数值部分越大,真值越大(更靠近 00),绝对值越小

# 定点数表示

计算机中的表示形式:

定点数的范围:

数轴表示:

  • 对于定点整数,最小刻度间距是 11
  • 对于定点小数,最小刻度间距是 2n{2^{-n}}

当数据大于最大正数时,发生正上溢;当数据小于最小负数时,发生负上溢;所有不在数轴刻度上的纯小数都超出了定点小数所能表示的精度,无法表示,此时发生精度溢出

习题

在执行语句时,yy 会被符号扩展成 FFFF FFF7H

所以 0000 007FH + FFFF FFF7H = 0000 0076H ,最高位的 11 被舍弃

# 浮点数表示

# 表示形式

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

表示为两部分

  • 阶码 EE
    1. 阶码 EE 是定点整数
    2. 阶码的位数决定数据表示的范围,位数越多,数据表示范围越大
  • 尾数 MM
    1. 尾数 MM 是定点小数
    2. 在阶码长度相同时,尾码的位数越多,精度越高

# 规格化

规格化是指使尾数真值的最高有效位为 1

有两种规格化数:

  1. 尾数的绝对值应大于或等于 (0.1)2(0.1)_2(0.5)10{(0.5)_{10}}

  2. 尾数的绝对值大于 11,小于 22

    N=2E×M=2±e×(±1.m), (1M<2)N=2^{E}\times M=2^{\pm e}\times(\pm1.m) ,\ (1\leq|M|<2)

  3. ❗️ 在实际表示的时候将最高有效位上的 1 隐藏

因为规格化后要求尾数的数值位的最高位为 11,所以要求如下:

# 表示范围

正数:

  • 最大值:阶码最大值,尾数为正数最大值
  • 最小值:阶码最小值,尾数为正数最小值

负数:

  • 最大值:阶码最小值,尾数为负数最大值
  • 最小值:阶码最大值,尾数为负数最小值

阶码每变化一个刻度,数据就变大一倍,所以刻度不均匀

非规格化

因为阶码是用移码表示的,移码的范围是

[2m,2m1][-2^m,2^m-1]

2m-2^m 对应的移码是: 0 00...00

2m12^m-1 对应的移码是: 1 11...11

因为尾数是用补码表示的定点小数,定点小数的补码范围是:

[1,12n][-1, 1-2^{-n}]

负数最小值 1-1 对应的补码是: 1 00...00

负数最大值 2n-2^{-n} 对应的补码是: 1 11...11

正数最小值 2n2^{-n} 对应的补码是: 0 00...01

正数最大值 12n1-2^{-n} 对应的补码是: 0 11...11

规格化

要求尾码的数值位的第一位为 11

在补码表示的情况下,正数的尾数应为 01... ,负数的尾数应为 10...

# IEEE 754 单精度浮点数

32 位单精度浮点数:

数符 SS:表示浮点数的符号

阶码 EE 采用移码表示,偏移量是 127127

  • 偏移量越大,移码最大值最小值越小,非规格化精度越高,规格化数表示范围越小
  • 采用 127127 偏移量的原因是使得任何一个规格化数的倒数能用另一个浮点数表示

尾数 MM 为定点小数,完整形式为 1.M1.M

  • 尾数用原码表示,最高数值位始终是 11
  • 小数点在尾数域的最前面

浮点数真值:

N=(1)S×(1.M)×2E127N=(-1)^S\times(1.M)\times 2^{E-127}

64 位双精度浮点数:

# 特殊数据的表示

浮点数不能精确表示 0

x=(1)S×(1.M)×2e,e=E127x=(-1)^S\times (1.M)\times 2^e,e=E-127

+0+0 的机器码对应的真值为:

1.0×21271.0\times 2^{-127}

0-0 的机器码对应的真值为:

1.0×2127-1.0\times 2^{-127}

当浮点数的指数为允许的最小指数值,即 emin=126e_{min}=-126 时,尾数不必是规范化的

0.0001×2125=0.001×21260.0001\times 2^{-125}=0.001\times 2^{-126}

# 习题

# 数据信息的校验

# 码距与校验

两个编码对应二进制位不同的个数称为码距

一个有效编码集中,任意两个码字的最小码距称为该编码集的码距

码距 检错、纠错能力
1 de+1d\geq e+1 可检测 ee 个错误
2 d2t+1d\geq2t+1 可纠正 tt 个错误
3 de+t+1,e>td\geq e+t+1,e>t 可检测 ee 个错误并纠正 tt 个错误

# 奇偶校验

# 简单奇偶校验

增加一位校验位 PP,使得最终的校验码中数字 11 的个数为奇数或偶数,其最小码距为 22

奇校验码

  • 整个校验码中 11 的个数是奇数(包含校验位)

  • 校验位

    P=D1D2...DnP=\overline{D_1\oplus D_2...\oplus D_n}

  • 接收方收到 D1D2...DnPD_1'D_2'...D_n'P',检错位G=0G=0 时正确

    G=D1D2...DnPG=\overline{D_1'\oplus D_2'...\oplus D_n'\oplus P'}

偶校验码

  • 整个校验码中 11 的个数是偶数(包含校验位)

  • 校验位

    P=D1D2...DnP=D_1\oplus D_2...\oplus D_n

  • 接收方收到 D1D2...DnPD_1'D_2'...D_n'P',检错位G=0G=0 时正确

    G=D1D2...DnPG=D_1'\oplus D_2'...\oplus D_n'\oplus P'

# 交叉奇偶校验

将待编码的原始数据信息构造成行列矩阵式的矩阵式结构,同时进行行和列两个方向的奇偶校验

奇偶校验可以检测出

  • 所有 33 位或 33 位以下的错误
  • 奇数位错误
  • 大部分偶数位错误( 44 个出错位恰好位于矩形的 44 个顶点的时候不行)

# 海明校验

SEC 码:最小码距为 33,只能纠正一位错误的海明码

# 校验位的位数

海明校验码 Hn...H2H1H_n...H_2H_1nn

包含原始信息 Dk...D2D1D_k...D_2D_1kk 位,校验位 Pr...P2P1P_r...P_2P_1rr 个偶校验组,要求 r2r\geq 2

rr 位的检错信息构成 rr 位的检错码 Gr...G2G1G_r...G_2G_1,可指出 2r12^r-1 种一位错

所以要求

N=k+r2r1N=k+r\leq 2^r-1

称为 (N,k)(N,k) 海明码

# 编码分组规则

校验位 PiP_i 应该放在检验码 HiH_i22 的幂次上,比如 H1,H2,H4...H_1,H_2,H_4...

那么每一个校验位的二进制形式中都只有 11 位是 11,那么对于 P2kP_{2^k} ,其他所有的 HiH_iii 的二进制位中第 kk 位是 11 的就和 P2kP_{2^k} 是一组

海明码映射关系:

H1 H2 H3 H4 H5 H6 H7

P1 P2 D1 P3 D2 D3 D4

发送端:

P1=D1D2D4P2=D1D3D4P3=D2D3D4P_1=D_1\oplus D_2\oplus D_4\\ P_2=D_1\oplus D_3\oplus D_4\\ P_3=D_2\oplus D_3\oplus D_4

接收端:

G1=P1D1D2D4G2=P2D1D3D4G3=P3D2D3D4G_1=P_1'\oplus D_1'\oplus D_2'\oplus D_4'\\ G_2=P_2'\oplus D_1'\oplus D_3'\oplus D_4'\\ G_3=P_3'\oplus D_2'\oplus D_3'\oplus D_4'

# 检测与纠错

Gr...G2G1=0G_r...G_2G_1=0 时,海明码大概率正确;当出错位数大于等于最小码距时,检错码也可能是 00

Gr...G2G10G_r...G_2G_1\neq 0 时,在假定只有一位出错的情况下,根据检错码的值找到编码中的出错位并取反纠正

无法检测是一位错还是两位错

# 扩展海明码

最小码距是 44,可以同时检测两位错,并纠正一位错

增加了一个总偶校验位 PallP_{all},用于区分是一位错还是两位错

总偶校验位 PallP_{all} 和 总偶校验检错码 GallG_{all} 的公式如下:

Pall=(D1D2...Dk)(P1P2...Pr)Gall=Pall(D1D2...Dk)(P1P2...Pr)P_{all}=(D_1\oplus D_2...\oplus D_k)\oplus (P_1\oplus P_2...\oplus P_r)\\ G_{all}=P'_{all}\oplus(D_1'\oplus D_2'...\oplus D_k')\oplus (P_1'\oplus P_2'...\oplus P_r')

假设无 33 位以上错误

  • 如果 Gall=1G_{all}=1,表示出现一位错
    • 根据 Gr...G2G1G_r...G_2G_1 的值指出出错位号,将其取反,即可纠错
  • 如果 Gall=0G_{all}=0 时,无错或者有偶数个错
    • Gr...G2G1=00..0G_r...G_2G_1=00..0 时,接受的无错;否则有两个错

# 循环冗余校验 CRC

#22 运算

在运算过程中不考虑进位

22 除法,在余数首位为 11 时上 11;首位为 00 时上 00

用 Python 代码实现如下:

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
def mod2_div(dividend, divisor):
"""
二进制模2除法(CRC校验用)
:param dividend: 被除数
:param divisor: 除数
:return: 余数(二进制字符串)
"""
# 将输入转换为列表方便操作
dividend = list(dividend)
divisor = list(divisor)
len_divisor = len(divisor)

# 被除数的当前处理位置
current_pos = len_divisor

# 初始化余数为被除数的前 len_divisor 位
remainder = dividend[:current_pos]

# 主循环:逐位处理被除数
while current_pos <= len(dividend):
# 如果余数首位是1,则执行异或(模2减)
if remainder[0] == '1':
# 余数与除数逐位异或
for i in range(len_divisor):
remainder[i] = '1' if remainder[i] != divisor[i] else '0'

# 移除余数首位的0(相当于左移)
remainder.pop(0)

# 如果未处理完被除数,则从被除数中取下一比特
if current_pos < len(dividend):
remainder.append(dividend[current_pos])

current_pos += 1

# 返回余数(二进制字符串)
return ''.join(remainder)


# 示例使用
if __name__ == "__main__":
dividend = "1000"
divisor = "1011"
remainder = mod2_div(dividend, divisor)
print(f"余数: {remainder}")

# 编码规则

CRCCRC 码长度共为 nn

原始数据信息为 Ck1Ck2...C0C_{k-1}C_{k-2}...C_0kk 位,用信息多项式 M(x)M(x) 表示

校验位 Pr1Pr2...P0P_{r-1}P_{r-2}...P_0rr

需满足

n=k+r2r1n=k+r\leq 2^r-1

生成多项式 G(x)G(x)

  • r+1r+1
  • 最高位和最低为必须为 11
  • 当传送信息任何一位出现错误,被生成多项式做除后余数应不为 00
  • 不同位发生错误,余数不同
  • 对不为 00 的余数继续进行模 22 除运算,应该使余数不为 00,使得余数循环

校验位的初始值:

  • 先让校验位为全 00
  • 除以生成多项式得到余数
  • 这个余数就是校验位

# 检错

模 2 余数

  • 如果余数为 0,通常认为数据无错误;若余数非 0,则说明检测到错误。
  • 余数补 0:当余数不为 0 时,通过补 0 扩展余数,继续模 2 除法,直到余数达到特定值(如 101)或完成全部校验。

循环左移与错误定位

  • 校验码循环左移:将被检测的校验码循环左移,同时余数继续模 2 运算。循环左移的目的是将错误位逐步移动到特定位置(如 A1 位置)。
  • 异或运算纠错:当余数为特定值(如 101)时,说明当前左移后的出错位已到达 A1 位置。此时通过异或运算(XOR)直接纠正该位。

重复过程直到完成

  • 纠错后,继续循环左移和模 2 除法,直到所有错误位被纠正且出错位回到原始位置。此时校验完成,数据已修复。