Post

数据的机器级表示及校验

数据的机器级表示及校验

整数的二进制数表示

二进制中,1表示正数,0表示负数,行不行?

原码表示

原码表示法的核心规则:

  1. 用最高位表示符号:0 表示正数,1 表示负数。
  2. 其余位表示该数的绝对值。
  3. 因此,零有两种表示形式:+0 (000...0) 和 -0 (100...0)。

我们以 4位二进制 为例来说明。4位原码能表示的范围是:-7 到 +7。


4位原码表示的数轴图

下图直观地展示了原码在数轴上的分布。请注意零的两种表示形式,这是原码最显著的特点。

image-20250924195739796

图解分析:

  1. 对称性
    • 数轴以 0 为中心,几乎完全对称(除了0本身)。
    • 每一个正数(除+0)都有一个对应的负数。例如,+3 (0011) 和 -3 (1011),它们的区别仅在于最高位的符号位。
  2. 零的表示
    • 这是原码表示法的“瑕疵”。0000 被定义为 +01000 被定义为 -0
    • 在数轴上,它们占据同一个点,但却有两个不同的二进制编码。这会导致计算和比较时需要额外的逻辑来判断。
  3. 绝对值与编码
    • 将一个数的原码表示去掉符号位,剩下的部分就是其绝对值的二进制形式。
    • 例如:
      • +5 -> 0 101 -> 绝对值是 101 (5)
      • -5 -> 1 101 -> 绝对值也是 101 (5)

原码的优缺点从数轴可见:

  • 优点:非常直观。人类很容易理解其表示方式,从二进制码直接就能看出数值的大小和正负。
  • 缺点
    • 运算复杂:从数轴上看,计算 2 + (-3) 需要经过以下步骤:
      1. 判断符号不同。
      2. 比较绝对值 |2|和|-3|(即3) 谁大。
      3. 用大的绝对值减去小的绝对值:3 - 2 = 1。
      4. 结果符号与绝对值大的数相同,即负号。
      5. 最终结果为 -1。 这个过程无法用简单的加法器电路完成,硬件实现效率低
    • 存在两个零:浪费了一个编码空间,并带来歧义。

这个数轴清晰地表明了为什么原码虽然易于理解,但却不是计算机存储有符号数的理想方案。补码通过重新映射负数的位置,完美解决了上述问题。

补码表示

4位补码表示的数轴图

image-20250924195727556

补码的数轴不再是像原码那样对称的,而是一个模循环。下图展示了这种独特的结构:

image-20250924195915419

图解分析:

  1. 唯一的零
    • 补码中,零只有一种表示形式:0000。这是补码相对于原码的第一个巨大优势。
  2. 负数的重新映射
    • 这是补码最精妙的设计。负数被“放置”在了正数区域的上方
    • 注意看数轴,-1 的编码是 1111-21110,……,一直到 -81000
    • 如果我们把这些负数的补码当作无符号整数来解释,它们的值分别是 15, 14, 13, … , 8。
    • 你会发现一个关键规律:一个负数的补码,其无符号值 = 模数 (16) + 该负数的真值
      • 例如:-5 的补码是 1011。将其视为无符号数,值是 11。而 16 + (-5) = 11。完美吻合!
  3. 模运算与循环数轴
    • 4位系统的模是 2^4 = 16。补码的数轴是一个首尾相接的圆(模16循环)
    • 加法是顺时针移动减法是逆时针移动
    • 计算 7 - 2 可以理解为 7 + (-2)
      • 7 的位置是 0111
      • -2 的补码是 1110(从图上看,它位于 7 的“上方”,其无符号值是14)。
      • 将两者相加:0111 (7) + 1110 (14) = 1 0101
      • 由于只有4位,最高位的1溢出被丢弃,剩下 0101,也就是 5。结果正确!
    • 这个“溢出”在模运算中是合理的,因为 7 + 14 = 21,而 21 mod 16 = 5
  4. “取反加一”的几何解释
    • 求一个数 X(例如 5, 0101) 的相反数 -X,就是在数轴上找到一个数 Y,使得 X + Y = 0 (mod 16)
    • 如何找到这个 Y?从 X 出发,需要走多远才能绕一圈回到0?
    • 路径长度 = 模数 (16) - X。
    • “取反加一”就是这个路径的快捷方式:
      • 取反0101 -> 1010 (值是10)。这相当于 (15 - 5) = 10
      • 加一10 + 1 = 11。而 16 - 5 正好等于 11
    • 所以,1011 (11) 就是我们要找的 -5 的补码。

补码的核心优势从数轴可见:

  • 统一了加减法:CPU不需要知道数是正还是负,也不需要减法器。只需要一个加法器,就可以完成所有加减运算。计算 A - B 时,CPU只需要计算 A + (B的补码),然后丢弃溢出位即可。这是硬件设计上的巨大简化。
  • 消除了“-0”0000 唯一表示0。1000 这个编码被赋予了新的含义:-8。这也解释了为什么补码的负数范围比正数多一个(-8 ~ -1, 0, +1 ~ +7)。

总结对比:

特性原码 (Sign-Magnitude)补码 (Two’s Complement)
零的表示两种 (+0, -0)一种 (0)
加减法操作需要复杂逻辑,判断符号和大小一个加法器通吃所有情况
数轴结构对称(除0外)模循环
硬件需求复杂极其简单
直观性非常直观不直观,需要理解模概念

正是因为补码在硬件实现上的巨大优势,它成为了现代计算机表示有符号整数的绝对标准。它的设计是计算机工程学中“用软件的复杂性(理解概念)换取硬件的极简性”的经典范例。

取反加一

1. “取反”:求的是 (2^k - 1) - x

对于一个 k 位(包括符号位)的二进制数 x(这里 x 是正数的原码):

  • 2^k - 1 是一个 k 位全为 1 的二进制数。例如,4位系统中,2^4 - 1 = 15,即 1111
  • x 的每一位进行取反(01, 10),得到的结果我们称为 ~x(按位取反)。

这个取反操作 ~x 的数学本质是什么? 它就是 (2^k - 1) 这个最大值减去 x 的值。 ~x = (2^k - 1) - x

例子(4位系统,求 -5 的补码):

  • x = 5 的原码: 0101
  • 2^4 - 1 = 15 (二进制 1111)
  • 取反操作 (~x)0101 -> 1010
  • 数学验证15 (1111) - 5 (0101) = 10 (1010)。结果 1010 正是取反的结果。

所以,“取反”这一步完成了绝大部分工作,它得到了 (2^k - 1) - x

2. “加一”:是为了得到最终的 2^k - x

我们已经通过“取反”得到了 (2^k - 1) - x。 而我们的最终目标是得到 x 的补码,即 -x 的机器数表示,其数学定义是 2^k - x(在模 2^k 的系统下)。

让我们比较一下目标和当前结果:

  • 目标2^k - x
  • 当前结果(2^k - 1) - x

很容易发现,目标和当前结果之间正好相差 1(2^k - x) - [(2^k - 1) - x] = 1

因此,“加一”就是为了弥补这个 1 的差距! (2^k - 1) - x + 1 = 2^k - x

继续上面的例子:

  • 取反后的结果: 1010 (值是 10)
  • 加一操作1010 + 1 = 1011
  • 数学验证16 (10000) - 5 (0101) = 11 (1011)。结果 1011 正是 -5 的补码。

补码的真值

对于一个 n 位的补码机器数:
\(X = -x_{n-1} \times 2^{n-1} + \sum_{i=0}^{n-2} x_i \times 2^i\)

  • \(x_{n-1}\):最高位(符号位)
    • 若为 1,则 \(-x_{n-1} \times 2^{n-1} = -1 \times 2^{n-1}\),提供一个巨大的负权重(例如 n=8 时,是 -128)。
    • 若为 0,则该项为 0,不影响结果。
  • \(\sum_{i=0}^{n-2} x_i \times 2^i\):剩余数值位按无符号S二进制数解析的值。

浮点数的二进制数表示

浮点数的二进制表示是一种精巧的权衡艺术,它通过符号位、阶码(带偏移)和尾数(规格化隐含前导1) 的三段式结构,用有限的二进制位实现了对广大实数范围的近似表示。IEEE 754标准统一了这种表示方法,并通过非规格化数特殊值等设计,增强了数值计算的健壮性。舍入规则则确保了近似过程的精确性和可预测性。

浮点数二进制表示的核心思想

浮点数用于表示实数,其核心思想是采用科学计数法的二进制版本,以在有限的位数内同时表示极大、极小的数以及高精度的数。通用格式为:

\(\pm S \times B^E\)

  • ±:符号位(Sign)
  • S:尾数/有效数(Significand/Mantissa)
  • B:基数/底(Base),通常为2(二进制下隐含,不存储)
  • E:阶码/指数(Exponent)

1. 规格化数 (Normalized Numbers)

image-20250924195955666

为了消除同一数值的多种表示形式(如 0.110 × 2⁵110 × 2²),浮点数采用规格化表示。

  • 规格化形式± 1.bb...b × 2^E
    • 尾数的最高位总是1(即整数部分为1)。
    • 这个前导1是隐含的,不需要在存储的尾数字段中表示,从而节省一位,提高精度。
  • 阶码的存储:阶码 E 的真实值需要加上一个偏移量(Bias) 再存入阶码字段。这使其总为正数,便于无符号比较。
    • 例如,8位阶码的偏移量是127,真实值 E = 0 则存储为 127E = -1 存储为 126

2. 表示范围与精度权衡

对于固定长度的浮点数格式(如32位单精度),其表示范围和精度之间存在权衡:

  • 增加阶码位数:➡️ 扩大表示范围,⬇️ 降低精度(尾数位数减少)。

  • 增加尾数位数:➡️ 提高表示精度,⬇️ 缩小范围(阶码位数减少)。

  • 采用更大的基数B:可以实现更大的范围,但可能会提高或降低精度。

    采用更大基数(B)对浮点数的影响总结

    1. 表示范围扩大
      基数B越大,阶码权重越高((B^E)),相同阶码位数下可表示范围显著扩大。
    2. 精度变化取决于数值大小
      • 小数值(E为负):精度提高
        相邻可表示数间隔更小(如B=16时,\(16^E\)衰减更快,间隔\(\Delta v = B^{E-S+1}\)更小)
      • 大数值(E为正):精度降低
        相邻可表示数间隔更大(如B=16时,\(16^E\)增长更快,间隔更大)

3. 非规格化数 (Denormalized Numbers)

image-20250924200038956

用于解决下溢(Underflow) 问题,即数值太小,无法用规格化形式表示(指数已达到最小值)。

  • 作用:填补0与最小规格化数之间的“空隙”,实现渐进下溢
  • 形式:当阶码字段为全0时,尾数的隐含位变为0(而非1),即表示为 ± 0.bb...b × 2^(E_min)
  • 优点:避免了因计算结果小于最小规格化数而被直接舍入为0的情况,保留了更多的数值信息。

即使在“可表示范围(Expressible Numbers)”内,也绝不是所有实数都能被精确表示的。浮点数只能表示这个范围内的有限个离散的数值点。

下图非常清晰地展示了这一点:数轴上的黑点代表可精确表示的浮点数,点与点之间存在着无法精确表示的“空隙”(Gaps)。

image-20250924200048183

那么,什么时候会用到非规格化数呢?

非规格化数解决的并不是“所有数都能精确表示”的问题(这是不可能的),而是解决“在0附近如何表示”的问题。

我们可以把“可表示范围”分成三个连续的区间来看:

  1. 规格化数区间 (Normalized Numbers)
    • 范围:从 [±2^{-126})[±(2-2^{-23})×2^{128}](对于单精度)。 * 特点:这个区间的数用规格化形式表示(隐含前导1)。数值越大,点与点之间的间隔(即精度)也越大。
    • 问题:这个区间无法表示0和比 2^{-126} 更小的数,这就留下了“下溢空隙”。
  2. 非规格化数区间 (Denormalized/Subnormal Numbers)
    • 范围:从 ±2^{-149}(单精度最小值)到 just under ±2^{-126}
    • 作用专门用来填补“规格化数区间”和“0”之间的那个巨大空隙。它通过牺牲精度(尾数没有隐含前导1,有效位数变少)来换取范围的扩展,使得表示能力能够“平滑地”、“渐进地”下降到0,而不是突然跌落到0。 * 所以,非规格化数本身就是“可表示范围”的一部分,是表示极小数的手段。
  3. 零点 (Zero)
    • 这是一个特殊的、唯一能精确表示的点。

核心结论与类比

情况数学结果浮点表示解释
落在黑点上e.g., 0.5, 2.0精确表示这个数正好是一个可表示的浮点数。
落在黑点之间e.g., 0.1, 3.1415926舍入这个数无法精确表示,会被舍入到最近的那个黑点(可表示的浮点数)。这是最常见的情况。
落在“下溢空隙”e.g., 1.0e-40非规格化数 或 flush to zero如果没有非规格化数,它会被舍入为0。非规格化数的存在,就是把这个“空隙”也填满了黑点,使得这些极小数也能被表示(尽管精度很低)。
超出数轴范围e.g., 1.0e40溢出 (Overflow)结果太大(或太小),超出了可表示范围,会被表示为无穷大(Infinity)。

一个简单的类比: 想象一把分辨率有限的尺子。

  • 规格化数:就像尺子上从1厘米开始的标准刻度,间隔均匀但越来越大。
  • 非规格化数:就像在尺子的0厘米到1厘米之间,手工添加的一系列不均匀的、更密集的微小刻度。没有它们,0到1厘米之间的任何测量值都只能被记录为“0”。有了它们,我们虽然量得不准,但至少知道它比0大,是一个非零值。
  • 舍入:就像你用这把尺子去测量一个长度,读数和真实长度之间总有误差,你只能读到最接近的那个刻度值。

所以,非规格化数并不是用来让所有数都能被精确表示的,而是专门为了在0附近提供一种表示能力,避免极小的非零值被直接当作0处理,从而保证数值计算在0附近的健壮性和连续性。

4. IEEE 754 标准

该标准是浮点数表示的工业规范,定义了两种基本格式:

参数单精度 (32位)双精度 (64位)
总位数3264
符号位1位1位
阶码位宽8位11位
阶码偏移+127+1023
尾数位宽23位(存储23位,隐含1位)52位(存储52位,隐含1位)

截屏2025-10-18 10.24.08

特殊值表示:

IEEE 754 还规定了一系列特殊值的位模式,用于处理异常情况:

  • ±0:阶码和尾数全为0,符号位区分正负零。

  • ±∞:阶码全为1,尾数全为0。

  • NaN (非数):阶码全为1,尾数非0。用于表示无效操作的结果(如 0/0, √(-1))。

    • 静默NaN (qNaN):不触发异常的NaN,用于传播。

    规则: 尾数域的最高位 F[0] 为 1

    特点: 这是最常见的NaN类型。大多数产生非法操作的情况(如 sqrt(-1))都会返回QNaN。CPU在运算中遇到QNaN时,通常会安静地将其作为结果返回。

    • 通知NaN (sNaN):用于触发异常的NaN。

    规则: 尾数域的最高位 F[0] 为 0,并且尾数域的其他部分至少有一位是1(以确保整个尾数不为零)。

    特点: 一旦SNaN参与任何算术运算(除了一些简单的赋值和拷贝),硬件就应该抛出一个无效操作异常

机器如何区分规格化数和非规格化数

核心原理:通过指数字段的特模式来区分

机器并不需要为每个数额外存储一个标志位来说明“我是规格化数”或“我是非规格化数”。它通过解读指数域(Exponent Field)的二进制模式来自动完成这种区分。

我们以最普遍的单精度(32位)浮点数为例,它的结构是:[1位符号位 S][8位指数位 E][23位尾数位 F]

机器遵循以下规则来解码:

  1. 如果指数位 E 不全为0且不全为1 (0 < E < 255)
    • 这是一个规格化数(Normalized Number)
    • 实际指数 = E - 偏置(Bias)。对于单精度,偏置是127,所以实际指数范围是 -126+127
    • 实际尾数 = 1.F。即在23位尾数 F 之前隐含着一个前导的1。这就是“规格化”的含义,通过调整指数,使得数字总是以 1.xxx... 的形式表示。
  2. 如果指数位 E 全为1 (E == 255)
    • 如果尾数位 F 全为0,这是一个无穷大Infinity),正负由符号位 S 决定。
    • 如果尾数位 F 不全为0,这是一个NaN(Not a Number),表示无效操作结果(如 0/0, √(-1))。
  3. 如果指数位 E 全为0 (E == 0)
    • 这是一个非规格化数(Subnormal/Denormalized Number)
    • 实际指数 = 1 - 偏置(Bias)。对于单精度,就是 1 - 127 = -126注意:这里不是 0 - 127
    • 实际尾数 = 0.F。即在23位尾数 F 之前隐含着一个前导的0,而不是1。

举例说明:如何区分和表示

假设我们有一个单精度浮点数,其二进制表示为: 0 00000000 10000000000000000000000 (S=0, E=0, F=10000000000000000000000)

  1. 机器如何区分?
    • 机器读取8位指数 E00000000
    • 发现它全为0,因此立即判定:这是一个非规格化数
  2. 机器如何计算它的值?
    • 符号位 S=0,所以是正数。
    • 实际指数 = 1 - 127 = -126
    • 实际尾数 = 0.F = 0.10000000000000000000000 (二进制)
    • 计算其数值: Value = (-1)^S × (实际尾数) × 2^(实际指数) = + × (0.1₂) × 2^(-126) = (2^(-1)) × 2^(-126) = 2^(-127)

这个数 2^(-127) 是一个非常接近0的正数。如果没有非规格化数,规格化数能表示的最小正数是当 E=1 (实际指数-126) 和尾数 M=1.0...0 时,即 1.0 × 2^(-126)2^(-127) 比它小了一半,正是非规格化数填补了0到 2^(-126) 之间的空白。

再看一个规格化数作为对比: 0 00000001 10000000000000000000000 (S=0, E=1, F=10000000000000000000000)

  1. 机器如何区分?
    • 机器读取指数 E=1 (00000001)。
    • 它既不全0也不全1,因此判定:这是一个规格化数
  2. 机器如何计算它的值?
    • S=0,正数。
    • 实际指数 = 1 - 127 = -126
    • 实际尾数 = 1.F = 1.10000000000000000000000 (二进制)
    • 数值 = (1.1₂) × 2^(-126) = (1 + 2^(-1)) × 2^(-126) = 1.5 × 2^(-126)

总结与类比

你可以把指数字段想象成一个开关

指数 E 的二进制模式机器解读的类型隐含的前导位实际指数计算
00000000非规格化数01 - Bias
0000000111111110规格化数1E - Bias
11111111无穷大 (Inf) 或 NaN

所以,你的机器通过一个简单的硬件电路检查指数位是否全0,来瞬间区分一个数是规格化还是非规格化。 这个过程完全由硬件自动完成,对程序员和用户是透明的。

这种设计的精妙之处在于:

  1. 无缝衔接:最大的非规格化数是 (1 - 2^{-23}) × 2^{-126},最小的规格化数是 1.0 × 2^{-126},它们非常接近,实现了从非规格化数到规格化数的平滑过渡。
  2. 节省位空间:没有浪费任何一位来单独存储类型标志。指数域的模式既表示了范围,也表示了类型。
  3. 硬件高效:判断一组位是否全0或全1在硬件层面是非常快速和简单的操作。

5. 舍入 (Rounding)

核心概念

在浮点数运算中,真实的结果往往无法用有限的尾数位精确表示(例如,1/10 这个简单的十进制小数在二进制中也是无限循环的)。因此,必须通过“舍入”操作,将其近似为最接近的可表示的浮点数。这个过程是浮点数算术的基础,也是计算误差的主要来源之一。

IEEE 754 标准定义的4种舍入模式

  1. 就近舍入 (Round to Nearest, ties to even)
    • 规则:舍入到最接近的可表示值。当待舍入的数值恰好位于两个可表示值的正中间时,则舍入到那个“偶数”方向(即其最低有效位为0)的值。
    • 特点:这是默认的、最常用的舍入模式。它在统计上最精确,因为它使舍入误差的期望值为零,并且不会引入明显的统计偏差。
    • 示例:假设我们使用4位尾数(二进制)表示,要舍入的数值为 1.0011 (即1.1875)。
      • 它处于 1.001 (1.125) 和 1.010 (1.25) 的正中间。
      • 1.001 的尾数最后一位是1(奇数),1.010 的最后一位是0(偶数)。
      • 因此,根据“ties to even”规则,应舍入到 1.010
  2. 向 +∞ 舍入 (Round toward +∞)
    • 规则:总是向正无穷大的方向进行舍入。结果会大于或等于真实值。
    • 应用:也称为“向上取整”(ceil)。常用于区间上下界计算、避免结果低估的金融计算等。
  3. 向 -∞ 舍入 (Round toward -∞)
    • 规则:总是向负无穷大的方向进行舍入。结果会小于或等于真实值。
    • 应用:也称为“向下取整”(floor)。常用于需要确定上限的场景。
  4. 向 0 舍入 (Round toward 0)
    • 规则:直接简单地丢弃无法表示的位,也称为“截断”(Truncation)。
    • 特点:对于正数,其行为同“向 -∞ 舍入”;对于负数,其行为同“向 +∞ 舍入”。总的效应是使结果的绝对值总是变小。
    • 应用:这种模式计算速度最快,但容易产生系统性偏差,因为误差总是偏向于零。

总结与对比

舍入模式别名方向误差特点主要应用
就近舍入 (RN)银行家舍入最近无偏、精度高通用计算(默认)
向 +∞ 舍入 (RU)向上取整总为正有偏、结果>=真值区间计算、金融上限
向 -∞ 舍入 (RD)向下取整总为负有偏、结果<=真值区间计算、金融下限
向 0 舍入 (RZ)截断朝向零有偏、结果|值|变小快速计算、旧式系统

二进制编码的十进制数表示

自然BCD码(NBCD):

0-9:0000-1001

+/-:1100/1101;0/1

差错

把一个数D放进存储器再拿出来,就不是D了,而是D’,那我们怎么才能判断这个值有没有发生改变呢?

image-20250924200945845

我们需要一个参照,但是没有原来的\(D\),那怎么办?

我们需要一个校验码 \(C\),但同时,我们没有办法保证\(C\)一定不出错,不然就不需要\(C\)了!

纠错的难点,在于怎么定位到哪个\(0/1\)出了问题

image-20250924201003453

奇偶校验码

基本思想:增加1位校验码来表示数据中1的数量是奇数还是偶数

0异或的时候等于那个数本身,而1是“对对碰”

数据输入:

奇校验:𝐶=𝐷𝑀⊕⋯⊕𝐷2⊕𝐷1⊕1

偶校验:𝐶=𝐷𝑀⊕⋯⊕𝐷2⊕𝐷1

数据输出:

奇校验:𝐶′′=𝐷’𝑀⊕⋯⊕𝐷’2⊕𝐷’1⊕1

偶校验:𝐶′′=𝐷’𝑀⊕⋯⊕𝐷’2⊕𝐷’1

如果C’和C’‘异或的结果为0,那么就说明这个数的输出没有问题(默认不会出现概率极其低的两位及以上出错)

海明码

基本思想:将数据分成几组,对每一组都使用奇偶校验码进行检错

image-20250924201019066


截屏2025-10-18 11.52.11

截屏2025-10-18 11.52.41

构造海明码的步骤

我们以传输一个4位的数据 1101 为例。

第一步:确定校验位的数量 (r)

公式: \(2^r \geq m + r + 1\)

  • \(m\) = 数据位长度 (我们的例子中是 4)
  • \(r\) = 所需校验位的最小数量

我们来尝试:

  • 如果 \(r = 2\): \(2^2 = 4\), \(4 + 2 + 1 = 7\) -> \(4 \geq 7\)? 不成立
  • 如果 \(r = 3\): \(2^3 = 8\), \(4 + 3 + 1 = 8\) -> \(8 \geq 8\)? 成立

所以,我们需要 3 个校验位。总码字长度为 \(4 + 3 = 7\) 位。

第二步:确定校验位 (P) 和数据位 (D) 的位置
  1. 位置从 1 开始编号,到 7。
  2. 所有位置是 2的幂次方 (1, 2, 4, 8…) 的位置,被校验位 (P) 占据。
  3. 剩下的位置由数据位 (D) 按顺序填充。
位置1234567
类型P1P2D1P3D2D3D4
我们的数据待计算待计算1待计算101

所以,我们的数据位 D1 D2 D3 D4 (也就是 1 1 0 1) 被放置在了位置 3, 5, 6, 7。 现在我们需要计算 P1, P2, P3 的值。

第三步:确定每个校验位负责哪些数据位

这是海明码最巧妙的部分。规则是: 位置为 (P_n) 的校验位,负责校验所有那些位置编号的二进制表示中,第 (n) 位为 1 的数据位。

  • P1 (位置 1,二进制 001):
    • 它负责所有位置编号二进制形式中第1位(最低位)为1的位。
    • 这些位置是:1, 3, 5, 7
    • 所以 P1 负责:P1, D1, D2, D4
  • P2 (位置 2,二进制 010):
    • 它负责所有位置编号二进制形式中第2位为1的位。
    • 这些位置是:2, 3, 6, 7
    • 所以 P2 负责:P2, D1, D3, D4
  • P3 (位置 4,二进制 100):
    • 它负责所有位置编号二进制形式中第3位为1的位。
    • 这些位置是:4, 5, 6, 7
    • 所以 P3 负责:P3, D2, D3, D4

用表格表示更清晰:

校验位二进制位置负责的位(位置)
P10011, 3, 5, 7
P20102, 3, 6, 7
P31004, 5, 6, 7
第四步:计算每个校验位的值

我们使用偶校验(即让负责的位中‘1’的个数为偶数)。

  • 计算 P1 (负责 P1, D1, D2, D4 -> 位置 1, 3, 5, 7):
    • 数据:? (P1), 1 (D1), 1 (D2), 1 (D4)
    • P1 ⊕ 1 ⊕ 1 ⊕ 1 必须为 0。
    • P1 ⊕ 1 必须为 0。 (1 ⊕ 1 = 0)
    • 所以 P1 = 1
  • 计算 P2 (负责 P2, D1, D3, D4 -> 位置 2, 3, 6, 7):
    • 数据:? (P2), 1 (D1), 0 (D3), 1 (D4)
    • P2 ⊕ 1 ⊕ 0 ⊕ 1 必须为 0。
    • P2 ⊕ 0 必须为 0。
    • 所以 P2 = 0
  • 计算 P3 (负责 P3, D2, D3, D4 -> 位置 4, 5, 6, 7):
    • 数据:? (P3), 1 (D2), 0 (D3), 1 (D4)
    • P3 ⊕ 1 ⊕ 0 ⊕ 1 必须为 0。
    • P3 ⊕ 0 必须为 0。
    • 所以 P3 = 0
第五步:组合成最终的海明码

现在我们将所有位填入位置:

位置1234567
类型P1P2D1P3D2D3D4
1010101

所以,为数据 1101 构造的 (7,4) 海明码是:1 0 1 0 1 0 1


如何校验和纠错?

假设接收方收到了这个码字 1010101。他会怎么做?

  1. 重新计算校验:
    • 他同样按照第三步的规则,用接收到的数据位重新计算三个校验值。
    • 计算 C1 (P1的组:1,3,5,7): 1 ⊕ 1 ⊕ 1 ⊕ 1 = 0 -> 通过 (0)
    • 计算 C2 (P2的组:2,3,6,7): 0 ⊕ 1 ⊕ 0 ⊕ 1 = 0 -> 通过 (0)
    • 计算 C3 (P3的组:4,5,6,7): 0 ⊕ 1 ⊕ 0 ⊕ 1 = 0 -> 通过 (0)
  2. 形成指错字 (Syndrome):
    • 将三个校验结果按 C3 C2 C1 的顺序排列,得到二进制数 000
    • 000 表示 没有错误

假设出错的情况: 如果传输中第5位(D2)从1变成了0,接收方收到 1010001

  • C1 (位 1,3,5,7): 1 ⊕ 1 ⊕ 0 ⊕ 1 = 1 -> 失败 (1)
  • C2 (位 2,3,6,7): 0 ⊕ 1 ⊕ 0 ⊕ 1 = 0 -> 通过 (0)
  • C3 (位 4,5,6,7): 0 ⊕ 0 ⊕ 0 ⊕ 1 = 1 -> 失败 (1)
  • 指错字 = C3 C2 C1 = 1 0 1
  • 指错字 101 的十进制是 5,这直接告诉我们第5位出错了。接收方只需将第5位翻转(0变回1),错误就得到了纠正。

循环冗余校验

基本思想

利用基于模2除法的“余数”特性,为数据块生成一个紧凑的校验码,任何对数据的修改都极有可能改变这个余数,从而被接收方发现。

假设数据有M位,左移数据K位(右侧补0),并用K+1位生成发送方和接收方事先约定的生成多项式除它(模2运算)

采用K位余数作为校验码

把校验码放在数据(不含补的0)后面,一同存储或传输

校错

模 2 除法

  • 每一步用当前余数的最高位决定是否与除数做 XOR(异或)。
  • 被除数从左到右逐位(或按除数长度)处理。
  • 如果当前部分最高位是 1,则用除数 XOR;如果最高位是 0,则用 000…(和除数等长的 0)XOR(其实就是左移)。
  • 商:当前部分最高位是 1 则商 1,是 0 则商 0(但 CRC 中我们主要关心余数)。

如果M+K位内容可以被生成多项式除尽,则没有检测到错误;否则,发生错误

例:100011000➗1001

image-20250924201034467

流程:原始数据附加n个0与生成多项式进行模二除法得到n位余数(CRC)用CRC码替换附加的0发送最终帧

This post is licensed under CC BY 4.0 by the author.

Trending Tags