最近,在看汇编。其中的一章引起了我的兴趣。那就是浮点处理和指令编码。大家其实在编写代码时候经常会用到浮点处理。大家又想过处理器是怎么处理这些数据的?希望通过本帖的简单介绍能对你有所帮助。
浮点数的几种表示方法
浮点二进制表示
浮点数是由三部分组成的:符号、尾数、指数。以数字 -1.23456X10^5为例,其中“-”是符号,表示该浮点数是负数,1.23456是尾数,5是指数。
IEEE二进制浮点数的表示
Intel微处理器使用IEEE发布的Standard754-1985 for Binary Floating-Point Arithmetic中规定的三种浮点二进制存储格式。见下表。
格式 说明
单精度 32位:符号占1位、指数占8位、尾数中的小数部分占23位。
可表示的大致范围2^-126~2^127,也称为短实数
双精度 64位:符号占1位、指数占11位、尾数中的小数部分占52位。
可表示的大致范围2^-1022~2^1023,也称为长实数
扩展精度 80位:符号占1位、指数占16位、尾数中的小数部分占63位。
可表示的大致范围是2^-16382~2^16383,也称为扩展实数
三种格式非常相似,因此这里重点讲述单精度格式。见下图。图中最高有效位(MSB)在左边:
1 8 23
符号 指数 尾数中的小数部分
符号
二进制浮点数的符号由一个符号位便是,如果该位为1表示负数,为0表示正数。浮点数的0是正数。
尾数
在形如mXb*e的表达式表示的浮点数中,m称为尾数,b是基数,e是指数。尾数部分m是一个十进制小数。大家知道的二进制,十进制和十六进制的加权进位表示法。现在可以把同样的概念扩展到数字的小数部分。例如十进制数123.154可以表示为表达式的和:
123.154=(1X10^2)+(2X10^1)+(3X10^-0)+(1X10^-1)+(5X10^-2)+(4X10^-3)
二进制浮点数的表示方法类似,只要用2为基数计算位权值即可。例如浮点数二进制值11.1011可表示为:
11.1011=(1X2^1)+(1X2^0)+(1X2^-1)+(0X2^-2)+(1X2^-3)+(1X2^-4)
另一种表示小数点后面的值的方法是用以2的幕为分母的分数之和表示,例如
.1011=1/2+0/4+1/8+1/16=11/16
得到分数表示的方法是相当直观的。二进制数1011实际上是十进制数11,可用1011(十进制数11)做分数的分子。假设e是二进制小数点后的有效数字的位数,那么分母就是2^e。在该例子中e=4,因此分母2^e=16。下表中给出了另外一些二进制浮点表示转换成十进制分数的例子。表1中的最后一项是23位规格化尾数所以能存储的最小分数。表2中列出了另一些例子,例子中分别给出了二进制浮点数以及与其等价的分数和十进制数。
表1 二进制浮点数转换成分数
二进制浮点数 十进制分数值
11.11 3 3/4
101.0011 5 3/16
1101.100101 13 37/64
0.00101 5/32
1.011 1 3/8
0.00000000000000000000001 1/8388608
表2 二进制和十进制分数
二进制浮点数 十进制分数值 十进制值
.1 1/2 .5
.01 1/4 .25
.001 1/8 .125
.0001 1/16 .0625
.00001 1/32 .03125
尾数的精度
连续实数的整体不可能以有限个数据位的浮点格式表示。例如,假设使用一种简化的格式表示浮点数,其中尾数占5个数据位,那么就没有办法表示二进制数1.1111和二进制数10.0000之间的实数了。因此表示二进制数1.11111就需要更高精度的尾数。类似的结论可以推广到IEEE双精度格式,53位的尾数不能表示需要54或更多个数据位的二进制数字。
指数
单精度浮点数的指数是以8位无符号正数的格式存储的,实际存储的是指数值与127相加的和。以数字1.101X2^5为例,指数(5)与127相加的和(十进制132,二进制10100010)存储在指数部分。下表给出了一些指数的例子。其中第一列是有符号十进制整数,第二列是调整后的值,最后一列是调整后的整数对应的二进制值。调整后的指数部分总是正数(在1~254之间),
实际指数范围是-126~+127。之所以选择这个范围,是为了避免最小可能的指数的倒数发生溢出(这里由于如果最小的指数选择了-127,那么-127+127=0)
指数(E) 调整后(E+127) 二进制值
+5 132 10000100
0 127 01111111
-10 117 01110101
+127 254 11111110
-126 1 00000001
-1 126 01111110
二进制浮点数的正规化
大多数浮点二进制数都是以正规化的格式存储,以便能够使得尾数的精度最大化。对于任何给定的浮点二进制数,可通过移动小数点,使小数点前仅有一个数字“1”从而使其正规化,指数表示小数点向左(正数)或向右移动(负数)的位数。看下面例子
未正规化格式 正规化格式
1110.1 1.1101X2^3
.000101 1.01X2^-4
101001. 1.010001X2^6
非正规化值:二进制浮点正规化操作的逆过程称为逆正规化,可通过移动二进制小数点直到指数为零。如果指数部分使正数n,那么向右移动小数点n位;如果指数部分是负数n,那么向左移动小数点n位。如果需要,开头的空位应以0填充。
IEEE表示法
实数编码
一旦符号位、指数和尾数域进行了正规化和编码,得到一个完整的二进制IEEE短实数就非常容易了。例如,二进制值1.101X2^0可表示如下:
符号位:0 (因为是正数)
指数 :01111111 (因为指数是0 0+127=127=01111111)
小数 :10100000000000000000000 (因为单精度短实数尾数中小数部分占23位)
调整后的指数01111111是十进制数127,所有正规化后的二进制尾数在小数点前面都有一个1,因此对该位就无需显式编码了。下表中给出了几个例子:
单精度浮点数位编码的例子
二进制 调整后的指数值 符号、指数和尾数
-1.11 127 1 01111111 11000000000000000000000
+1101.101 130 0 10000010 10110100000000000000000
-.00101 124 1 01111100 01000000000000000000000
+100111.0 132 0 10000100 00111000000000000000000
+0000001101011 120 0 01111000 10101100000000000000000
IEEE规范中还规定了几类实数和非实数的编码:
正数0和负数0
反向正规化的有限数
正规化的有限数
正无穷和负无穷
非数字值(NaN)
不确定数
Intel浮点单元使用不确定数响应一些无效的浮点操作。
正规化和反向正规化:正规化的有限数是0到无穷大之间的所有可以编码为正规化实数的非0有限值。尽管乍看起来所有非零浮点数都可以正规化,不过事实上在值非常接近于0时这是不可能的。由于指数范围的限制,FPU有可能不能把二进制的小数点移动到正规化的位置。假设FPU的计算结果是1.0101111X2^-129,其指数太小无法在单精度实数中存储,这时将产生一个下溢出异常,可通过向左移动小数点(每次一位)使其正规化,直到指数落在有效的范围内为止:例
1.01011110000000000001111 X 2^-129
0.10101111000000000000111 X 2^-128
0.01010111100000000000011 X 2^-127
0.00101011110000000000001 X 2^-126
在这个例子中,由于移动了小数点,精度会有一些损失。
正无穷和负无穷:正无穷(+∞)表示最大的正实数,负无穷(-∞)表示最小的负实数。可以比较无穷值和其他值:-∞小于+∞,-∞小于任何有限数,+∞大于任何有限数。正无穷和负无穷都能用来表示溢出。
非数字(NaN):NaN是不表示任何有效实数的位序列。IA-32体系结构包含两种类型的NaN:quiet NaN可通过大多数算术运算而不会导致任何异常;signalling NaN可用于产生一个无效操作异常。编译器可以用Signalling NaN值填充未初始化的数组,对该数组进行任何计算的企图都会引发一个异常。quiet NaN可用于存放调试会话产生的诊断信息。浮点单元不会试图对NaN执行操作。IA-32手册详细描述了一套规则,用于确定以这两种类型的NaN组合作为操作数时指令执行的结果。
特殊值的编码:下表列出了浮点操作中经常遇到的几个特殊值的编码。标记位的位置既可以是0,也可以试1。QNaN表示quiet NaN , SNaN表示 signalling NaN。
特殊的单精度浮点值的编码
值 符号、指数、尾数
正数0 0 00000000 00000000000000000000000
负数0 1 00000000 00000000000000000000000
正无穷 0 11111111 00000000000000000000000
负无穷 1 11111111 00000000000000000000000
QNaN x 11111111 1xxxxxxxxxxxxxxxxxxxxxxxxx
SNaN x 11111111 1xxxxxxxxxxxxxxxxxxxxxxxxx
*SNaN的尾数域以0开始,但是其余x表示的域中至少要有一个为1。
把十进制分数转换为二进制实数
如果十进制分数可以很容易地表示为1/2+1/4+1/8+......的格式,那么就很容易得到其对应的二进制实数。下表中的左边一列中的大部分分数一眼上去都不容易转换成二进制,不过如果写成第二列的格式就容易多了。
十进制分数和二进制实数的例子
十进制分数 分解为 二进制实数
1/2 1/2 .1
1/4 1/4 .01
3/4 1/2+1/4 .11
1/8 1/8 .001
7/8 1/2+1/4+1/8 .111
3/8 1/4+1/8 .011
1/16 1/16 .0001
3/16 1/8+1/16 .0011
5/16 1/4+1/16 .0101
很多实数,如1/10(0.1)或1/100(.01),不能用有限个二进制数字位表示,这样的分数只能近似表示为若干以2的幕为分母的分数之和。想想货币值如$39.95会受什么影响。
另一种方法:使用二进制长除法。把十进制分数转换成二进制数时如果涉及到的十进制值比较小,可以使用一种很方便的方法:首先把分子和分母都转换成二进制值,然后再进行除法操作。例如,十进制值0.5可表示成分数5/10,十进制5是二进制值0101,十进制值10是二进制值1010,执行下面的除法,我们发现商是二进制值0.1:
_____.1__________
1010 | 0101.0
- 101 0
___________
0
在被除数减去1010后余数为0时,除法终止。因此十进制分数5/10等于二进制值0.1,我们称上面这种方法为二进制长除法。PS来自于DePaul大学的Harvey Nice。
例子:以二进制数表示0.2。下面把十进制的0.2(2/10)转换成二进制值,使用长除法。首先,把二进制值10除以1010(十进制值10):
___.00110011(等等)_________________
1010 | 10.00000000
1 010
__________
1100
1010
___________
10000
1010
____________
1100
1010
_____________
等等
第一个足够大的余数是10000,在除以1010之后,余数是110,在末尾添加一个0,新的被除数是1100,除以1010后,余数是10,再添加3个0,新的被除数是10000,这等于进行第一次除法操作时被除数的值。从这点开始,商中重复出现0011,因此我们得知准确的商是无法得到的。0.2不能用有限个二进制位表示。0.2的单精度编码的尾数部分是00110011001100110011001
把单精度值转换成十进制数
下面是在把IEEE单精度值转换成十进制数时的推荐步骤:
1。如果MSB(最高有效位)是1,该值为负数,否则为正数。
2。接下来的8位表示指数,减去二进制值01111111(十进制值127),得到未调整的值数值,把未调整的指数值转换成十进制数。
3。接下来的23位表示尾数,注意尾数小数点前面有一个被省略的“1.”,尾数尾部的0可以忽略。这时就可以使用第一步中得到的符号位、第二步中得到的指数以及本步骤中得到的尾数创建一个浮点二进制数了。
4。对第三步得到的浮点数进行逆规格化操作(根据指数相应地移动小数点,指数为正则右移动,指数为负则左移动)。
5。从左到右,使用位权表示方法得到浮点数的2的次幕之和的表示,进而得到相应的十进制数。
例子:把IEEE浮点数(0 10000010 01011000000000000000000) 转换成十进制数
1. 该数是正数。
2. 未调整的指数值是00000011,也就是十进制数3。
3. 组合符号位、指数和尾数得到二进制数+1.01011X2^3
4. 逆正规化后的二进制数是+1010.11
5. 对应的十进制值是+10 3/4或+10.75
好长的一帖。我打字打的都快晕了。不过这里只是简单介绍了浮点数的转换。当然后面还有应用。今天就先到这里吧。我休息一下。大家也能消化一下。还有表中数值对齐问题是在弄晕我了。希望斑竹帮忙调试一下。谢谢阅读。下一篇介绍FPU 未完待续......
- 标 题:浮点处理和指令编码(更新完毕)
- 作 者:install
- 时 间:2009-07-23 11:36:52
- 链 接:http://bbs.pediy.com/showthread.php?t=94153