浮点数


浮点数

文章插图
浮点数【浮点数】浮点数是属于有理数中某特定子集的数的数字表示,在计算机中用以近似表示任意某个实数 。具体的说,这个实数由一个整数或定点数(即尾数)乘以某个基数(计算机中通常是2)的整数次幂得到,这种表示方法类似于基数为10的科学计数法 。
基本介绍中文名:浮点数
外文名:Floating Point
範围:有理数
定义:近似
数值:一个整数或定点数
浮点数浮点计算是指浮点数参与的运算,这种运算通常伴随着因为无法精确表示而进行的近似或捨入 。一个浮点数a由两个数m和e来表示:a = m × b^e 。在任意一个这样的系统中,我们选择一个基数b(记数系统的基)和精度p(即使用多少位来存储) 。m(即尾数)是形如±d.ddd...ddd的p位数(每一位是一个介于0到b-1之间的整数,包括0和b-1) 。如果m的第一位是非0整数,m称作规格化的 。有一些描述使用一个单独的符号位(s 代表+或者-)来表示正负,这样m必须是正的 。e是指数 。结构由此可以看出,在计算机中表示一个浮点数,其结构如下:尾数部分(定点小数) 阶码部分(定点整数)阶符±阶码e数符±尾数m这种设计可以在某个固定长度的存储空间内表示定点数无法表示的更大範围的数 。浮点加法减法运算设有两个浮点数x和y,它们分别为x = Mx*2^Exy = My*2^Ey其中Ex和Ey分别为数x和y的阶码,Mx和My为数x和y的尾数 。两浮点数进行加法和减法的运算规则是设 Ex小于等于Ey,则 x±y = (Mx*2^(Ex-Ey)±My)*2^Ey,完成浮点加减运算的操作过程大体分为四步:1. 0 运算元的检查;2. 比较阶码大小并完成对阶;3. 尾数进行加或减运算;4. 结果规格化并进行捨入处理 。⑴ 0 运算元检查浮点加减运算过程比定点运算过程複杂 。如果判知两个运算元x或y中有一个数为0,即可得知运算结果而没有必要再进行后续的一系列操作以节省运算时间 。0运算元检查步骤则用来完成这一功能 。⑵ 比较阶码大小并完成对阶两浮点数进行加减,首先要看两数的阶码是否相同,即小数点位置是否对齐 。若二数阶码相同,表示小数点是对齐的,就可以进行尾数的加减运算 。反之,若二数阶码不同,表示小数点位置没有对齐,此时必须使二数阶码相同,这个过程叫作对阶 。要对阶,首先应求出两数阶码Ex和Ey之差,即△E = Ex-Ey若△E=0,表示两数阶码相等,即Ex=Ey;若△E>0,表示Ex>Ey;若△E<0,表示Ex<Ey 。当Ex≠Ey 时,要通过尾数的移动以改变Ex或Ey,使之相等 。原则上,既可以通过Mx移位以改变Ex来达到Ex=Ey,也可以通过My移位以改变Ey来实现Ex=Ey 。但是,由于浮点表示的数多是规格化的,尾数左移会引起最高有效位的丢失,造成很大误差 。尾数右移虽引起最低有效位的丢失,但造成误差较小 。因此,对阶操作规定使尾数右移,尾数右移后阶码作相应增加,其数值保持不变 。显然,一个增加后的阶码与另一个阶码相等,增加的阶码的一定是小阶 。因此在对阶时,总是使小阶向大阶看齐,即小阶的尾数向右移位(相当于小数点左移)每右移一位,其阶码加1,直到两数的阶码相等为止,右移的位数等于阶差△E 。⑶ 尾数求和运算对阶结束后,即可进行尾数的求和运算 。不论加法运算还是减法运算,都按加法进行操作,其方法与定点加减法运算完全一样 。⑷ 结果规格化在浮点加减运算时,尾数求和的结果也可以得到01.ф…ф或10.ф…ф,即两符号位不等,这在定点加减法运算中称为溢出,是不允许的 。但在浮点运算中,它表明尾数求和结果的绝对值大于1,向左破坏了规格化 。此时将运算结果右移以实现规格化表示,称为向右规格化 。规则是:尾数右移1位,阶码加1 。当尾数不是1.M时需向左规格化 。⑸ 捨入处理在对阶或向右规格化时,尾数要向右移位,这样,被右移的尾数的低位部分会被丢掉,从而造成一定误差,因此要进行捨入处理 。简单的捨入方法有两种:一种是"0舍1入"法,即如果右移时被丢掉数位的最高位为0则捨去,为1则将尾数的末位加"1" 。另一种是"恆置一"法,即只要数位被移掉,就在尾数的末尾恆置"1" 。在IEEE754标準中,捨入处理提供了四种可选方法:就近捨入其实质就是通常所说的"四捨五入" 。例如,尾数超出规定的23位的多余位数字是10010,多余位的值超过规定的最低有效位值的一半,故最低有效位应增1 。若多余的5位 是01111,则简单的截尾即可 。对多余的5位10000这种特殊情况:若最低有效位现为0,则截 尾;若最低有效位现为1,则向上进一位使其变为 0 。朝0捨入 即朝数轴原点方向捨入,就是简单的截尾 。无论尾数是正数还是负数,截尾都使取值的绝对值比原值的绝对值小 。这种方法容易导致误差积累 。朝+∞捨入 对正数来说,只要多余位不全为0则向最低有效位进1;对负数来说则是简单的截尾 。朝-∞捨入 处理方法正好与 朝+∞捨入情况相反 。对正数来说,只要多余位不全为0则简单截尾;对负数来说,向最低有效位进1 。⑹ 溢出处理浮点数的溢出是以其阶码溢出表现出来的 。在加\减运算过程中要检查是否产生了溢出:若阶码正常,加(减)运算正常结束;若阶码溢出,则要进行相应处理 。另外对尾数的溢出也需要处理 。阶码上溢 超过了阶码可能表示的最大值的正指数值,一般将其认为是+∞和-∞ 。阶码下溢 超过了阶码可能表示的最小值的负指数值,一般将其认为是0 。尾数上溢 两个同符号尾数相加产生了最高位向上的进位,将尾数右移,阶码增1来重新对齐 。尾数下溢 在将尾数右移时,尾数的最低有效位从尾数域右端流出,要进行捨入处理 。实例题目例如,一个指数範围为±4的4位十进制浮点数可以用来表示43210,4.321或0.0004321,但是没有足够的精度来表示432.123和43212.3(必须近似为432.1和43210) 。当然,实际使用的位数通常远大于4 。特别数值此外,浮点数表示法通常还包括一些特别的数值:+∞和?∞(正负无穷大)以及NaN('Not a Number') 。无穷大用于数太大而无法表示的时候,NaN则指示非法操作或者无法定义的结果 。二进制表示众所周知,计算机中的所有数据都是以二进制表示的,浮点数也不例外 。然而浮点数的二进制表示法却不像定点数那幺简单了 。浮点数概念先澄清一个概念,浮点数并不一定等于小数,定点数也并不一定就是整数 。所谓浮点数就是小数点在逻辑上是不固定的,而定点数只能表示小数点固定的数值,具用浮点数或定点数表示某哪一种数要看用户赋予了这个数的意义是什幺 。C++中的浮点数有6种,分别是:float:单精度,32位unsigned float:单精度无符号,32位double:双精度,64位long double:高双精度,80位然而不同的编译器对它们的支持也略有不同,据我所知,很多编译器都没有按照IEEE规定的标準80位支持后两种浮点数的,大多数编译器将它们视为double,或许还有极个别的编译器将它们视为128位?!对于128位的long double我也仅是听说过,没有求证,哪位高人知道这一细节烦劳告知 。下面我仅以float(带符号,单精度,32位)类型的浮点数说明C++中的浮点数是如何在记忆体中表示的 。先讲一下基础知识,纯小数的二进制表示 。(纯小数就是没有整数部分的小数,讲给国小没好好学的人)纯小数要想用二进制表示,必须先进行规格化,即化为 1.xxxxx * ( 2 ^ n ) 的形式(“^”代表乘方,2 ^ n表示2的n次方) 。对于一个纯小数D,求n的公式如下:n = 1 + log2(D); // 纯小数求得的n必为负数再用 D / ( 2 ^ n ) 就可以得到规格化后的小数了 。接下来就是十进制到二进制的转化问题,为了更好的理解,先来看一下10进制的纯小数是怎幺表示的,假设有纯小数D,它小数点后的每一位数字按顺序形成一个数列:{k1,k2,k3,...,kn}那幺D又可以这样表示:D = k1 / (10 ^ 1 ) + k2 / (10 ^ 2 ) + k3 / (10 ^ 3 ) + ... + kn / (10 ^ n )推广到二进制中,纯小数的表示法即为:D = b1 / (2 ^ 1 ) + b2 / (2 ^ 2 ) + b3 / (2 ^ 3 ) + ... + bn / (2 ^ n )现在问题就是怎样求得b1,b2,b3,……,bn 。算法描述起来比较複杂,还是用数字来说话吧 。声明一下,1 / ( 2 ^ n )这个数比较特殊,我称之为位阶值 。例二例如0.456,第1位,0.456小于位阶值0.5故为0;第2位,0.456大于位阶值0.25,该位为1,并将0.456减去0.25得0.206进下一位;第3位,0.206大于位阶值0.125,该位为1,并将0.206减去0.125得0.081进下一位;第4位,0.081大于0.0625,为1,并将0.081减去0.0625得0.0185进下一位;第5位0.0185小于0.03125……最后把计算得到的足够多的1和0按位顺序组合起来,就得到了一个比较精确的用二进制表示的纯小数了,同时精度问题也就由此产生,许多数都是无法在有限的n内完全精确的表示出来的,我们只能利用更大的n值来更精确的表示这个数,这就是为什幺在许多领域,程式设计师都更喜欢用double而不是float 。float的记忆体结构,我用一个带位域的结构体描述如下:struct MYFLOAT{bool bSign : 1; // 符号,表示正负,1位char cExponent : 8; // 指数,8位unsigned long ulMantissa : 32; // 尾数,32位};符号就不用多说了,1表示负,0表示正指数是以2为底的,範围是 -128 到 127,实际数据中的指数是原始指数加上127得到的,如果超过了127,则从-128开始计,其行为和X86架构的CPU处理加减法的溢出是一样的 。比如:127 + 2 = -127;-127 - 2 = 127尾数都省去了第1位的1,所以在还原时要先在第一位加上1 。它可能包含整数和纯小数两部分,也可能只包含其中一部分,视数字大小而定 。对于带有整数部分的浮点数,其整数的表示法有两种,当整数大于十进制的16777215时使用的是科学计数法,如果小于或等于则直接採用一般的二进制表示法 。科学计数法和小数的表示法是一样的 。小数部分则是直接使用科学计数法,但形式不是X * ( 10 ^ n ),而是X * ( 2 ^ n ) 。拆开来看 。0 000000000000000000000000000000符号位 指数位 尾数位--------------------------------------------------------------------------------例三判断两个浮点数是否相等 。在这个例子中我们以C++代码来判别两个浮点数是否相等 。由于浮点数在存储中无法精确表示,所以 fp1==fp2 无法準确的判断float型变数fp1与fp2是否相等 。应该使用 (fp1-fl2)<0.0000001 来进行判断 。示例:bool equal(float fp1,float fp2){if( abs( fp1 - fp2 ) < 0.00000001 ) return true;elsereturn false;}--------------------------------------------------------------------------------导数字分布简介作者:concreteHAM什幺是浮点数,不用我多说,这里我们要讨论的是规格化的任意进制浮点数的前导数字的机率分布 。在《电脑程式设计艺术》第二卷中做了非常深入的讨论,这里我从中精炼出要点 。实例例如:2.345 E 67这是一个十进制规格化浮点数,前导数字就是2 。