文章目录
- 前言
- 一、定点格式
- 定点整数
- 二、浮点数的表示方法
- 2.1 浮点数存储
- 2.2 浮点数加减法
- a. 操作数检查
- b. 对阶
- c. 尾数相加与检查溢出
- d. 结果规格化与溢出处理
- e.舍入处理
- Some tips: 为什么进位/双符号判断法有效的通俗解释?
- Some tips:符号位拓展
前言
计算机中常用的数据表示格式有两种,一是定点格式,二是浮点格式。一般来说,定点格式容许的数值范围有限,要求的处理硬件比较简单。而浮点格式容许的数值范围很大,要求的处理硬件比较复杂。
一、定点格式
所谓定点格式,即约定机器中所有数据的小数点位置是固定不变的。由于约定在固定的位置,小数点就不再使用记号“.”来表示。原理上讲,小数点位置固定在哪一位都可以,但是通常将数据表示成纯小数或纯整数。
目前计算机中多采用定点纯整数表示,因此将定点数表示的运算简称为整数运算。
定点整数
- 一个正整数,当用原码、反码、补码表示时,符号位都固定为 0,用二进制表示的数位值都相同,即三种表示方法完全一样。
- 一个负整数,当用原码、反码、补码表示时,符号位都固定为 1,用二进制表示的数位值都不相同。此时由原码表示法变成补码表示法的规则如下:
(1)原码符号位为 1 不变,整数的每一位二进制数位求反得到反码;
(2)反码符号位为 1 不变,反码数值位最低位加 1,得到补码。
该种表示方法优点:
- 很好地使得+0与-0都为唯一格式存储
- 使得相同的的硬件用同样的方式可以同时处理加减法(减法看做正负数相加)
eg:
具体数学原理不在此赘述。
二、浮点数的表示方法
在计算机中,把一个数的有效数字和数的范围在计算机的一个存储单元中分别予以表示。这种把数的范围和精度分别表示的方法,相当于数的小数点位置随比例因子的不同而在一定范围内可以自由浮动,所以称为浮点表示法。
规定:任意一个十进制数 N 可以写成 N = 10^E . M
同样,在计算机中一个任意二进制数 N 可以写成 N = 2^e . M
。 M 称为浮点数的尾数,是一个纯小数。e 是比例因子的指数,称为浮点数的指数,是一个整数。
2.1 浮点数存储
IEEE754标准规定的 32 位短浮点数和 64 位长浮点数的标准格式,同时规定移码的偏置值是 2^(n-1)-1
- 由于基数 2 是固定常数,对每一个浮点数都一样,所以不必用显示表达(存储)它。
- 32 位的浮点数中,S 是浮点数的符号位,占 1 位,安排在最高位,S=0 表示正数,S=1表示负数。M 是尾数,放在低位部分,占用 23 位,小数点位置放在尾数域最左(最高)有效位的右边。E 是阶码,占用 8 位,阶符采用隐含方式,即采用移码方法来表示正负指数。采用这种方式时,将浮点数的指数真值 e 变成阶码 E 时,应将指数 e 加上一个固定的偏置常数 127,即 E=e+127。这种表示方法的好处是E可表示的数恒大于0(可看做为无符号数),便于比较不同数字之间的阶码大小。
- 64 位的浮点数中符号位 1 位,阶码域 11 位,尾数域 52 位,指数偏移值是 1023。
- 为了提高数据的表示精度,当尾数的值不为 0 时,尾数域的最高有效位应为 1,这称为浮点数的规格化表示。
所以在 IEEE754 标准中,一个规格化的 32 位浮点数 x 的真值表示为
x=(–1)S×(1.M)×2E–127, e=E–127
规格化的 64 位浮点数 x 的真值为 x=(–1)S×(1.M)×2E–1023, e=E–1023
对于单精度,最大的指数为127(0+127),最小的指数为-126(-255+127);
规格化的浮点数的尾数域最左位(最高有效位)总是 1,故这一位无需存储,而认为隐藏在小数点的左边。于是用 23/52 位字段可以存储 24/53 位有效数。
对 32 位浮点数 N,IEEE754 定义一些特殊情况(N为真值):
- 若 E=255 且 M!=0,则 N=NaN。符号 NaN (Not a Number)表示无定义数据,采用这个标志的目的是让程序员能够推迟进行测试及判断的时间,以便在方便的时候进行。
- 若 E=255 且 M=0,则 N=(–1)S∞。当阶码 E 为全 1 且尾数 M 为全 0 时,表示的真值 N 为无穷大,结合符号位 S 为 0 或 1,也有+∞和–∞之分。
- 若 0<E<255,则 N=(–1)S×(1.M)×2E–127(规格化数)。偏移值不选27=128(10000000),而选 27–1= 127(01111111)。阶码 E 的范围变为 1~254,指数值 e 则为–126~+127。因此 32 位浮点数表示的绝对值的范围是 10–38~1038。
- 若 E=0 且 M=0,则 N=(–1)S0。当阶码 E 为全 0 且尾数 M 也为全 0 时,表示的真值N 为零(称为机器 0),结合符号位 S 为 0 或 1,有正零和负零之分。
- 若 E=0 且 M!=0,则 N=(–1)S×(0.M)×2–126(非规格化数)。对于规格化无法表示的数据,可以用非规格化形式表示。(下图符号表述与本文有些不同,注意区分)
2.2 浮点数加减法
a. 操作数检查
- 若有一个操作数为NaN则直接返回NaN
- 若有一个操作数为0则直接返回另一个操作数
- 若有一个操作数为Infinity:
- 若另一个操作数也是Infinity且符号相同则返回第一个操作数
- 若另一个操作数也是Infinity且符号不同则返回NaN
- 若其他情况则返回Infinity
b. 对阶
两操作数可直接进行加减操作的前提是两者同阶。因此需要对不同阶的操作数进行小数点对齐,由于存储的尾数在右移的过程中损失精度的概率比左移要小(因为尾数的存储时未使用的位数是在有效位右边补0),所以我们采取小阶向大阶对齐
注意这里的阶码e指的是该数字在规格化二进制下的阶码,或者无法规格化时的阶码
For example:
0.65的32位存储 其阶码e实际为Exp-127=126-127=-1
-0.425的32位存储 其阶码e实际为Exp-127=125-127=-2
此时我们如果要对两者进行操作,就需要将-0.425二进制存储的尾数右移一位,阶码存储变为与0.65相同。
不必担心无法规格化的数,其阶数e规定为1-127=-126,已经为最小阶数,所以必然由其向另一操作数对齐。
c. 尾数相加与检查溢出
当我们对两个操作数完成上述操作,自然可以对其尾数进行相加。如果没有溢出,那么皆大欢喜。可惜世事不可尽如人意v_v
尤其需要强调的一点是:计算机中加减法实际都是采用加法硬件来进行计算,所以当出现负浮点数时,虽然负浮点数存储的二进制尾数依然是原码,但在计算时需要将其转化为补码来实现用加法的硬件完成操作
对于浮点数而言,当两个正操作数相加超过表示范围,称为上溢出;当两个负操作数相加超过表示范围,称为下溢出;(若溢出后的情况与定义的特殊情况相同,则按照特殊情况处理)
通常来说有两种方式判断溢出,分别是 进位判断法 和 双符号判断法。
- 进位判断法(采用单符号位:在计算数值前面补上符号位)
- 无溢出:符号位 和 最高位 一起进位或一起不发生进位。
- 溢出:符号位 或 最高位 其中一个发生进位。
- 双符号判断法(采用双符号位:在计算数值前面补两个符号位)
- 无溢出:计算后两符号位相同。
- 上溢出:计算后两符号位为01。
- 下溢出:计算后两符号位为10。
这里的计算数值是在尾数之前补上对阶之后的隐藏位,以上述为例
此时进行0.65+(-0.425)操作,必须强调的是,用于计算的负数尾数需要化为补码
~(11011001100110011001101)+1->00100110011001100110011
进位判断法(单符号位)
0-1-01001100110011001100110 单符号位-隐藏位-尾数 0.65
+ 1-0-00100110011001100110011 单符号位-隐藏位-尾数 -0.425
->0-0-01110011001100110011001 符号位与计算最高位都产生进位 无溢出
所以两操作数计算结果的隐藏位为0,符号数为0,
阶码为01111110(与操作数保持一致)
尾数为01110011001100110011001
双符号判断法
00-1-01001100110011001100110 单符号位-隐藏位-尾数 0.65
+ 11-0-00100110011001100110011 单符号位-隐藏位-尾数 -0.425
->00-0-01110011001100110011001 两个符号位都相同 无溢出
同上
但是我们并不将结果直接存储为0-01111110-01110011001100110011001,因为此时隐藏位为0未规格化,而且还可能需要舍入操作
对结果进行规格化 结果为0-01111100-11001100110011001100100 约等于0.225
d. 结果规格化与溢出处理
在尾数相加的过程中,尾数最高位可能产生进位,称为尾数上溢
对阶的过程中,尾数最低有效位可能被舍去,为尾数下溢
- 尾数上溢 两个同符号尾数相加产生了最高位向上的进位,要将尾数右移,阶码增 1
来重新对齐。 - 尾数下溢 在将尾数右移时,尾数的最低有效位从尾数域右端流出,要进行舍入处理(在步骤e中说)。
当出现尾数上溢时,需要向右规格化则尾数右移1位,阶码+1,因此浮点数相加时尾数的溢出也被称为可补救的。
若无溢出,则按照最基本的规格化方式,即规格化为1.xxx的形式(不考虑非规格化数)——1依然是隐藏位,xxx为存储的尾数二进制,阶码同步变化。(即所谓的左规,实际就是最普通的规格化过程)
在规格化时,阶码往往也会产生变化
- 阶码上溢 超过了阶码可能表示的最大值的正指数值,一般将其认为是+∞和–∞。
- 阶码下溢 超过了阶码可能表示的最小值的负指数值,一般将其认为是 0。
e.舍入处理
IEEE754 标准中,对舍入处理提供了四种可选办法。
- 朝 0 舍入 即朝数轴原点方向舍入,就是简单的截尾。无论尾数是正数还是负数,截
尾都使取值的绝对值比原值的绝对值小。这种方法容易导致误差累积。 - 朝+∞舍入 对正数来说,只要多余位不全为 0 则向最低有效位进 1;对负数来说,则
是简单的截尾。 - 朝–∞舍入 处理方法正好与朝+∞舍入情况相反。对正数来说,则是简单截尾;对负
数来说,只要多余位不全为 0,则向最低有效位进 1 - 向偶数舍入(就近舍入)(default)
这里着重说一下为什么这是默认的方法,以及具体使用方法:
如果我们总是把两个可表示值中间的数字向上舍入,采用这种方式舍入得到的一组数的平均值将比这些数本身的平均值略高一些。相反,那么舍入后的一组数的平均值将比这些数本身的平均值略低一些。这些方法会在计算这些值的平均数中引入统计偏差。向偶数舍入在大多数现实情况中避免了这种统计偏差。
因此假设我们想将十进制数舍入到最接近的百分位。不管用那种舍入方式,我们都将把1.2349999舍入到1.23,而将1.2350001舍入到1.24,因为它们不是在1.23和1.24的正中间。另一方面我们将把两个数1.2350000和1.2450000都舍入到1.24,因为4是偶数。
而在二进制中,每一位的权重都是前一位权重的二分之一,因此当一位为1(设其为第n位)时,实际我们知道n位及之后的尾数和一定大于等于第n-1位权重的一半,如下图例。因此我们说——在尾数右移时,被移去的最高数值位为0,则舍去。被移去的最高数值位为1,则在尾数末尾加1(舍0入1法);这样做可能使尾数又溢出,此时需要再做一次右规。,实际就是向偶数舍入。(如果你理解成简单的四舍五入是绝对错误的)
还有一种恒置1法,不在此赘述。
Some tips: 为什么进位/双符号判断法有效的通俗解释?
-
首先我们回忆一下补码转化为十进制的快捷方式:最高位取负值 其余位取正值 相加可得十进制。
如11001->转化为十进制-16+8+1=-7。所以实际上补码越大,实际值越靠近0(实际值的绝对值越小)
-
对于单符号位(进位)判断法:
- 正数加正数的情况下,由于符号位都是0,所以符号位不可能向前产生进位,所以溢出也就意味着数值最高位向前进位,最终导致结果符号位是1,计算结果出错。此时符号位没有进位,数值最高位进位。
- 负数加负数的情况,因为两个符号位均为1 ,所以必定会在符号位产生进位。当补码比较大时候,此时若数值最高位产生进位,实际两者之和绝对值很小,同时将符号位重新置1;只有当补码很小,实际值的绝对值很大时,剩下的数值位的最高位相加之后才有可能不向前进位,这时候符号位变成了0,也就是两个负数相加得到了正数,计算结果错误。
- 正负数相加情况此时也很容易理解了。所以该种方法判断溢出是符号位或最高位其中一个发生进位。
-
对于双符号判断法:
- 正数加正数,符号位相加为00,若数值位最高位产生进位,溢出时符号必然为01
- 负数加负数,符号位相加为10,若数值位最高位产生进位,则说明操作数的的补码很大,实际值很小,必然不会溢出,产生进位后符号位为11。
- 正数加负数,符号位相加为11,若数值位最高位产生进位,则说明正数很大,负数很小,结果也不会溢出,产生进位后符号位为00;若不产生进位,符号位依然为11。都不溢出
浮点数加减的基本结构示意图
Some tips:符号位拓展
- 对于有符号整数,例如如何将32位数据正确的拷贝到64位机器?我们只需要将它的符号位不断拷贝即可。对于正数,符号位为0,左边补0自然不影响大小。
- 对于负数,符号位为1,假设原符号位权重为2n,向左拷贝一位后新符号位权重为2n+1,但此时转换十进制时原符号位权重已经取正数,实际各位权重之和对数值本身并无影响