编码规则不完全指北

如无特别标注,均采用8 位二进制数编码举例。

原码

当x为整数时:

\[[x]_原 \begin{cases} 0,x &2^n > x \ge 0 \\ 2^n-x &0 \ge x>-2^n \end{cases} \]

当x为小数时:

\[[x]_原 \begin{cases} x &1> x \ge 0 \\ 1-x &0 \ge x>-1 \end{cases} \]

直观来讲,将真值转化为原码的过程是:

\[在逗号或小数点前加一位符号位,0表示负数,1表示负数。 \]

所以,8位二进制数原码可以表示的整数的范围是1,111 1111 ~ 0,111 1111即-127~127。

关于 0

需要注意的是,根据原码的定义,大括号里两个区间都包含了整数0 ,故0 的原码有两种转换方法。经过计算,我们可以看出用两种方法算出的0 的原码是不同的,我们将它们分别定义为 +0 和 -0。

\[[+0]_原 = 0,000\ 0000\\ [-0]_原 = 1,000\ 0000 \]

对人类来说,这造成了一定困扰,因为我们都知道整数0 只有一个,但表示它的原码却又两个,这个困扰将在补码中得到解决。

难题

对于加法器来说,更亟待解决的是原码的运算问题。从数轴中我们可以看出,正数和负数比较大小的逻辑是有区别的:正数的绝对值越大其本身的值就越大,负数的绝对值越大其本身的值就越小,但在逻辑只有0 和1 的计算机中,我们很难给它设计这些复杂的运算规则。我们从原码中选取一个较小的负数,再选取一个较大的正数,将它们相加,我们会发现结果产生了溢出,不再能用8 位二进制数原码表示,但我们又发现其真值结果依然处于可以用八位二进制数表达的区间里,这就产生了矛盾,于是反码出现了。

反码

当x为整数时:

\[[x]_反 \begin{cases} 0,x &2^n > x \ge 0 \\ (2^{n+1}-1)+x &0 \ge x>-2^n \end{cases} \]

当x为小数时:

\[[x]_反 \begin{cases} x &1> x \ge 0 \\ (2-2^{-n})+x &0 \ge x>-1 \end{cases} \]

直观来讲,将表示负数的原码转化为反码的过程是:

符号位保持不变,其他位按位取反。即将0 变成1 ,将1 成0。由于我们的计算机是不懂得“按位取反”这句话的,于是我们设计了一个巧妙的方法来求反:

\[欲对某一位数取反,即用1 减去这个数。 \]

例如对1011取反,计算1111 - 1011 即可。

8位二进制数反码可以表示的整数的范围是1,000 0000 ~ 0,111 1111即-127~127

反码的优势

用反码表示数时,终于不会出现原码“相加溢出”的情况了,计算机可以用反码进行加减法运算。

关于 0

和补码一样,反码中的0 也有两种表示方式:

\[[+0]_反 = 0,000\ 0000\\ [-0]_反 = 1,111\ 1111 \]

在反码的数轴上,依然对称分布着一个 +0和一个 -0,于是我们想:能不能人工将反码数轴的负数部分整体右移一位,让 -0和+0重合?然后,补码诞生了。

补码

当x为整数时:

\[[x]_补 \begin{cases} 0,x &2^n > x \ge 0 \\ 2^{n+1}+x &0 \ge x>-2^n \end{cases} \]

当x为小数时:

\[[x]_补 \begin{cases} x &1> x \ge 0 \\ 2+x &0 > x\ge-1 \end{cases} \]

直观来讲,将表示负数的 原码转化为原码的过程是:

\[符号位不变,其他按位取反,最后将结果+1。 \]

因为补码数轴相当于将反码数轴的负半轴整体右移一位,故补码能表示的负数多了一个,那就是1,000 0001,即-128。

所以,8位二进制数补码可以表示的整数的范围是1,000 0001 ~ 0,111 1111即-128~127。

关于 0!

在补码中,0 的问题终于得到了解决:

在补码数轴中,-0 往右移了一位,和 +0重合。

\[[0]_补 = 0,000 \ 0000 \]

移码

\[[x]_移 = 2^n + x \ \ \ (2^n > x \ge -2^n)\\(n为整数位数) \]

为了更好地比较真值的实际大小,我们设计了移码。通过加一个很大的数将所有负数转化为正数,让我们比较大小时候更直观。通俗来说,反码是为了实现“去符号化表示”而发明的编码方式。

关于 0

由于

\[[+0]_移 = 2^5 + 0 = 1, 00000\\ [-0]_移 = 2^5 - 0 = 1, 00000 \]

所以,移码中的0只有一种表示方法。

移码和补码的关系

由于移码和补码对负数的处理很相似,不过补码用了一位符号位,所以:

\[移码可以通过改变补码的符号位得到(将\ 1改成\ 0,将\ 0改成\ 1)。 \]

浮点数

通常,浮点数会被表示成

\[N = S * r^J\\S\ \ 为尾数\\ J\ \ 为阶码 \\ r\ \ 为基数(可取2、4、8、16等)\\ 写作\\ J_f \ \ \ \ J_1J_2J_3...J_m\ \ \ \ S_f\ \ \ \ S_1S_1S_3...S_n \\ 即\\ 阶符\ \ \ \ 阶码的数值部分\ \ \ \ 数符\ \ \ \ 尾数的数值部分 \]

浮点数的尾数部分用纯小数表示,为了提高精度,我们一般采用规格化来表示浮点数,所谓规格化即尾数最高位为 1的浮点数,规格化时,浮点数的精度达到最高,这其实很容易理解,省去了 1前面无数个 0,将 0的位数用阶码表示,尾数所能表示的字长自然就更多了,精度也因此提升。

当阶码数值位取 m位、尾数数值位取 n位,非规格化时,我们可以得到:

\[其可以表示的\\最大正数为 2^{(2^m-1)}*(1-2^{-n})\\最小正数为2^{-(2^m-1)}*2^{-n}\\最大负数为-2^{-(2^m-1)}*2^{-n}\\最小负数为-2^{(2^m-1)}*(1 - 2^{-n}) \]

当规格化时

\[其可以表示的\\最大正数为 2^{(2^m-1)}*(1-2^{-n})\\最小正数为2^{-(2^m-1)}*2^{-1}\\最大负数为-2^{-(2^m-1)}*2^{-1}\\最小负数为-2^{(2^m-1)}*(1 - 2^{-n}) \]

浮点数的优势和限制

  • 位数相同时,浮点数表示的范围比定点数大得多;
  • 当浮点规格化时,其精度比定点数高得多;
  • 浮点数运算步骤比定点数多,运算速度比定点数低,运算线路比定点数复杂,对硬件要求比定点数高;
  • 程序设计方面,定点数位了防止溢出,需要设置比例因子,而浮点数不需要。

IEEE 754

现在偶尔会翻出刚上大一时刚接触时对IEEE 754的幼稚理解,没想到这么快就过去了一年,轮到我真正学习IEEE 754了,希望现在我可以解释一年前自己的疑问。

IEEE 754,即 Institute of Electrical and Electronics Engineers 754 ,二进制浮点数运算标准,是为了方便软件移植而规定的国际标准。

IEEE 754中规定基数为2,第一位为符号位,8位阶码E用移码表示,23位尾数M规格化用原码表示,由于规格化后原码的最高数字位总是 1,故IEEE 754将这个 1作为默认值(即省略),这样就为尾数节省了一个数字位,提高了精度。

“127”

IEEE 754中,规定阶码的移码偏移值为 127,为什么是 127而不是126、128?

首先要先明白几个事实:

  • 移码的出现是为了去符号化。
  • 因为尾数中最高数字位的1 被作为默认值,所以无法表示0 ,故IEEE754规定阶码为0000 0000时,所表示的数值为0 ,特别地,当阶码为1111 1111时,所表示的数值为+∞。

8位二进制数(含一符号位)可以表示数的范围是-127 ~ +127,去掉IEEE754所预留的0000 0000 和1111 1111,可以表示数的范围变成了-126 ~ +127(中间去掉一个+0),为了去符号化,我们有三个偏移值可以选择:126、127、128。

我们先来看看移126码的情况:

当我们想表示-126时,移126码为 0000 0000,与IEEE754的预留规则相冲突。

再看看移128码的情况:

当我们想表示+127时,移128码为 1111 1111,与IEEE754的预留规则相冲突。

如果选择其他数字作为偏移值,会出现无法去符号化或数值溢出等问题。我们的选择只剩下了127。

posted @ 2021-09-28 23:03  4Get5th  阅读(114)  评论(0编辑  收藏  举报