一、基础概念
1、定义
- 浮点数表示法是指以适当的形式将比例因子表示在数据中,让小数点的位置根据需要而浮动。这样,在位数有限的情况下,既扩大了数的表示范围,又保持了数的有效精度。
2、浮点数的表示格式
- 浮点数表示为:N=r^E * M
- 其中 r 是浮点数阶码的底(隐含),与尾数的基数相同,通常 r=2. E 和 M 都是有符号的定点数,E 称为阶码,M 称为尾数。浮点数由阶码和尾数两部分组成,如下图:
- 阶码是整数,阶符 Jf 和阶码的位数 m 共同反映浮点数的表示范围及小数点的实际位置;数符 Sf 代表浮点数的符号;尾数的位数 n 反映浮点数的精度。
3、规格化浮点数
- 出于精度考虑,通常规定尾数的最高数位必须是一个有效值,因此规格化操作就是将非规格化浮点数的尾数和阶码进行调整,使其符合浮点数在尾数的最高数位上是一个有效值。
- 浮点数由于基数的不同,规格化操作也不太一样。
1)基数为 2:
- 尾数最高位为 1 的数称为规格化数;规格化时,尾数左移一位,阶码减一,称之为左规;尾数向右移一位,阶码加一,称之为右规。左规可能需要进行多次,右规只需进行一次。
2)基数为 4:
- 尾数最高两位不全为零的数称为规格化数;规格化时,尾数左移两位,阶码减一;尾数右移两位,阶码加一。
3)基数为 8:
- 尾数最高三位不全为零的数称为规格化数;规格化时,尾数左移三位,阶码减一;尾数右移三位,阶码加一。
4)基数为 2^n:
- 尾数最高 n 位不全为零的数称为规格化数;规格化时,尾数左移 n 位,阶码减一;尾数右移 n 位,阶码加一。
4、规格化数的表现形式
- 规格化浮点数的尾数 M 的绝对值应满足:1/r<=|M|<=1。
- 当 r=2,则有 1/2<=|M|<=1,规格化表示的尾数形式如下:
1)原码规格化
- 正数为 0.1****** 的形式,最大值表示为:0.111…1,最小值为:0.1000…0,尾数表示范围:1/2<=M<=(1-2^-n).
- 负数为 1.1******* 的形式,最大值:1.10…0,最小值:1.11…1,尾数表示范围 -(1-2^-n)<=M<=-1/2
2)补码规格化
- 正数的表示、最大最小值以及尾数的表示范围同原码正数。
- 负数为 1.0***** 的形式,最大值为:1.01….1,最小值为:1.00…0,尾数的表示范围 -1<=M<=(-1/2-2^-n).
- 可以说,基数为 2时,原码规格化尾数最高位一定是 1,补码规格化数的尾数最高位一定与尾数符号位相反。
5、IEEE 754 标准
1)何为 IEEE 754 标准
- IEEE二进制浮点数算术标准(IEEE 754)是20世纪80年代以来最广泛使用的浮点数运算标准,为许多CPU与浮点运算器所采用。这个标准定义了表示浮点数的格式(包括负零
-0)与反常值(denormal number)),一些特殊数值(无穷(Inf)与非数值(NaN)),以及这些数值的“浮点数运算符”;它也指明了四种数值舍入规则和五种例外状况(包括例外发生的时机与处理方式)。
2)表示形式
- 现代计算机中一般都采用这个标准来表示浮点数,使用这种标准表示的浮点数形式如下:
- IEEE 754 标准规定的常用浮点数格式有单精度 float 型的短浮点数、双精度 double 型的长浮点数和临时浮点数,如下表:
- 其中短浮点数和长浮点数,其尾数采用隐藏位策略(数值的最高位总是”1“,为了能使尾数多表示一位有效位,将这个”1“隐含,因此尾数实际上是 24 位)的原码表示,且阶码用移码表示的浮点数。临时浮点数又称扩展精度浮点数,无隐含位。
- 因为阶码是用移码形式存储的,因此存储浮点数阶码部分之前,需要将偏置值加到阶码真值上,例如,阶码真值为 3,在短浮点数中,移码表示的阶码为 127+3=130(82H);长浮点数中则是 1023+3=1026(402H)
3)真值
6、定点和浮点的区别
1)数的范围
- 相同字长的情况下,浮点数所能表示的范围远远大于定点表示。
2)精度
- 在精度方面,浮点数不如定点数。
3)运算
- 浮点数的运算比定点数复杂,因为既要做尾数的运算还要做阶码的运算,运算结果也需要规格化,因此对计算机的要求比较高,需要专门的器件。
4)溢出
- 浮点运算中,运算结果超出尾数表示范围不一定溢出,只有规格化后的阶码超出所能表示的范围时,才发生溢出。
二、浮点数的加减运算
- 进行浮点数运算,需要将阶码和尾数分开进行运算。浮点数的加减法统一采用补码进行操作,浮点数加减法的步骤一般分下面几步:
1、对阶
- 对阶的目的在于将两个数的小数点位置对齐,使得两个数的阶码相等,具体操作就是先算出两个数的阶差,然后将小阶的数进行尾数右移直到与大阶数同阶。尾数进行右移时,可能会舍弃掉有效位从而产生误差,影响精度。
2、尾数求和
- 将对阶后的尾数按定点数的加减法运算规则进行运算。
3、规格化
- 将运算结果进行规格化,使其符合尾数最高位为 1.以双符号为例:
1)尾数大于零时:
S = 00.1******
2)尾数小于零:
S = 11.0******
- 当尾数结果出现 00.0**** 或 11.1***** 的形式时,需要进行左规,也就是尾数左移一位,和的阶码减一,直到尾数的形式为:00.1**** 或 11.0****;而当尾数求和结果出现溢出,如尾数形如:10.**** 或 01.***** 时需要进行右规,也就是尾数右移一位,和的阶码加一。
4、舍入
- 对阶和右规时,可能将尾数低位丢掉,引起误差,影响精度,因此需要进行舍入。舍入的方法有:“0”舍“1”法和恒置“1”法。
1)“0”舍“1”法:
- 尾数右移时,若被移去的最高数值位为“0”,则直接舍去;若被移去的最高数值位为“1”,则在尾数末尾加“1”。
- 缺点是可能使尾数又溢出,此时再需要做一次右规。
2)恒置“1”法:
- 尾数右移时,不论丢掉的最高数值位是“0”还是“1”,都使右移后的尾数末位恒置“1”。缺点就是使尾数可能变大也可能变小。
5、溢出判断
- 浮点数的溢出与否,由阶码的符号决定。以双符号为例,若阶码符号位为“01”,即阶码大于最大阶码,表示上溢,进入中断处理;当阶码的符号位是“10”,即阶码小于最小阶码,表示下溢,按机器零处理。可以总结为:阶码符号位不同表示溢出,真实符号位和高位符号位一致。
三、浮点类型的转换
- 高级语言如 C/C++ 中的 float 和 double 对应 IEEE754 中的单精度浮点数和双精度浮点数,当数据类从 char->int->long->double 和 float->double 转换 一般不会出现损失,因为从前到后范围和精度呈从小到大的趋势;相反若是从高精度和大范围的数往低范围的数进行转换,便有可能出现损失。
1)double->float:
- 由于 double 是 8 字节的,而 float 是 4 字节,显然 float 能表示的数的范围和精度都比 double 的小,因此这种转换可能发生数据溢出和舍入。
2)float->int 或 double->int
- 数据类型 int 也是 4 个字节,但是 int 型没有小数,所以 float 类型或 double 类型的数据若是小数,就会直接被强制取整,例如 float 型的 4.25 转换成 int 型会直接从小数点出截断,直接变成 4;这种直接截取整数部分的操作,显然会影响数据的精度。另外一种情况是,float 型或 double 型数据不是小数,但是表示的数大于 int 所能表示的数,此时从 float 或 double 转换成 int 型就会发生溢出。
- 因此在编程时,一旦涉及从高范围和高精度类型数据往低范围和低精度的数据类型转换时,需要慎重处理。