在C语言中双精度浮点数线性化相等比较的研究

2017-10-13 04:23章国华
船电技术 2017年1期
关键词:线性化位数整数

章国华



在C语言中双精度浮点数线性化相等比较的研究

章国华

(武汉船舶职业技术学院,武汉430050 )

以LCC软件为设计基础,提出了双精度浮点数比较大小算法的设计方法,通过程序验证了算法的精确度,分别分析了绝对误差和相对误差方法设计算法,最后从双精度浮点数在计算机中的表示方法角度提出了浮点数线性化后相等比较改进的方法。

比较 算法 双精度 有效数字

0 引言

C程序设计的教科书中一般都不涉及浮点数的运算,浮点数的运算涉及到计算机的复杂的硬件结构和理解起来有一定难度的表示方法[1]。然而浮点数的运算在计算机软件系统中可以说是无处不在的。大多数编程语言都将浮点数据类型作为基本类型,从计算机的CPU硬件到软件编译器、操作系统都涉及到浮点数的运算。当然,在用各种编程语言实现各种应用程序设计的时候,即数据的处理,大都会碰到浮躁数的运算[2]。例如,在用C语言写应用软件的时候,各类整数的相等比较是直接使用“==”来判断,正是由于浮点数在计算机中表示与整数在计算机中的表示具有完全不一样的特殊性,浮点数的比较就完全不能简单的用“==”来实现。通常会利用差值的绝对值的精度来判断。

假设:f1和f2是两个浮点数,在C语言中规定有双精度精度DBL_EPSILON。

#defineDBL_EPSILON

2.2204460492503131e-016

/*smallest such that

1.0+DBL_EPSILON!=1.0*/

即可以用 fabs(f1-f2)<= DBL_EPSILON 来判断f1和f2是否相等。如果要求更高的精度,也不能简单地把DBL_EPSILON定得更小就行了。

因为,DBL_EPSILON是C语言编译器给出的一个不变的数据,也就是误差分析当中的绝对误差,使用一个完全固定的数值,对于双精度类型的数据可以表达的整个数域来说是有缺陷的。例如对于f1和f2大小分别是1e-100 、2e-100附近这样的数据的时候,它是不合适的,因为它们之差1e-100已经远小于DBL_EPSILON,两个不等的浮点数就可以判断是相等的,出现了相等比较的错误。即使DBL_EPSILON已经是最小的,而且它是由数据在计算机中表示时的位数决定的。例如,对于f1和f2大小是10和10.0000000000000000001这样的数据的时候,它也不合适,因为10和10.0000000000000000001两个数的绝对误差为1e-19,小于DBL_EPSILON,判断的结果当然是相等,也就出错了,因为两个数据的有效数字位数超过了16位。适合浮点数相等比较的情况只是f1或者f2在数值1附近的时候,函数的注释正好说明了这个问题,大范围的浮点数的比较需要解决。

1 双精度浮点数比较相等方法的改进

以上用绝对误差的方式比较浮点数的相等不合适,就用相对误差方式尝试解决浮点数相等比较的问题。相对误差的算法如下:

bool IsEqual(double a, double b, double relError )

{

return (( fabs ( (a-b)/a ) < relError )?TRUE :FALSE;)

}

这个浮点数比较相等的函数也是有问题的。用固定的函数的第一个形参做相对比较,在应用过程中,调用IsEqual(a, b, relError ) 和 IsEqual(b, a, relError ) 的时候,由于参数a和b的数量级不一样,得到的结果是不同的。显然,当实际调用的第一个参数是0的话,就有出现了除0溢出的错误,这样的相对误差比较显然也是不合适的。

显而易见,可以把除数选取为a和b当中绝对数值较大的就可以避免以上情况[3]:

bool IsEqual(double a, double b, relError )

{

if (fabs(a)

return ( fabs((a-b)/a) > relError ) ?TRUE : FALSE;

else

return (fabs( (a-b)/b) > relError ) ?TRUE : FALSE;

};

此算法就克服了绝对误差算法对于太小的浮点数不能比较的缺陷。

2 优化浮点数比较大小的实现方法

相对误差算法能处理很小的浮点数相等的比较,但浮点数的运算速度慢,能改用整数运算就快了。为了从根本上解决问题,需要仔细研究浮点数在计算机中的表示。根据IEEE754浮点数的内存结构,符号1位在最高位,指数11位在次高位,尾数52位在低位(不包括隐含的1位)。浮点数的大小对应的是其指数和尾数的大小,人工比较时,先比较符号,再比较指数,最后比较尾数,就可知两个浮点数是否相等。用C语言编程来比较浮点数的相等也应该是同样的道理。即根据浮点数的内存结构,按照整数来理解进行相等的比较,情况也是相同的。而且用这种方法对浮点数进行比较的话,因为作为整数运算效率比浮点数的高得多。比如

double f1 = 1.58;

double f2 = 1.57

根据以上的定义,事实上f1>f2 是成立,能否用一种方法,证明(long long int)f1 > (long long int)f2 也是成立的。根据IEEE754的浮点结构特点,不是所有的浮点数都可以精确的表达,可以精确表达的浮点数实际上是有限的,就是IEEE754枚举的232个,事实上,绝大多数的浮点数数值是不能在计算机里精确表达的。

目前虽然有80位的浮点数表示,这里只分析64位的情况(IEEE754)。尾数是53位的(暗含了第一个位数是1),对于可以精确表达的浮点数来说,对于IEEE754 单精度和双精度浮点数,能够精确表示的整数的范围为[4]:

如果把这53位当作整数来理解,对于隐含的1位,除浮点数零外,其它数都是1,不考虑也不影响比较的结果。对于任一浮点数,把它当作64位的整数,根据二进制计数原理,而且是线性分布的。这样,将两个浮点数看成是整数,不需要转换成整数,将其对应的整数做差值运算,得到的整数表明的是两个浮点数之间有多少个实际可以精确表达的浮点数的个数(对应的指数相同的情况下是不言而喻的;指数不同的时候,也是同样有效的,因为现在研究的是相等比较,不是大小比较,指数不同则涉及的是精确表达的浮点数的个数更多了,有利于相等的比较,而不会有相反的作用)。因此,对于两个正的浮点数,他们的大小比较就可以用 (long long int)f1 - (long long int)f2 来进行比较了,即将两个浮点数当成整型数来相减,其差值的结果实际上就相当于相对误差了,这个相对误差,不等同于普通意义上的相对误差,它所表达的是,两个浮点数之间可能还有多少个可以精确表达的浮点数。这样通过指定这个阈值来控制两个浮点数的比较就更有效了[5]。(long int)f1 - (long int)f2在这里只是算法的示意,C编译器会报错。具体实现见最后面的程序。

对于两个正的浮点数比较相等就有:

bool IsEqual(double f1, double f2, int precisionFloatNum)

{

if ( abs ( (long long int)f1 - (long longint)f2 ) < precisionFloatNum ) return TRUE;

}

这里要用整数的绝对值比较函数而不能用浮点数的绝对值比较函数,因为要将此时的浮点数看成是整数,否则就无法实现以上分析的目的。但是负整数和正整数之间现在还不能进行直接的比较,因为根据IEEE754的内存结构,正整数和负整数是不同的,对应的在计算机中表示的整数在整个区间是不连续的,正的最小的整数就是0,对应的计算机表示的整数是0x0000000000000000,负的最大的整数就是-0,与之对应的计算机表示的整数则是0x 8000000000000000,在IEEE754的表达当中是有两个0的,一个是 +0 一个是-0。而且按照 f1 == f2 的判断 +0和-0是相等的,根据浮点数对应的在计算机中表示的整数在整个区间的不连续性,具有以下特点,+0 和正的计算机表示的浮点数可以当成整数的方式直接进行比较,-0 和负的计算机表示的浮点数可以当成为整数的方式直接进行比较,如果采取措施将把他们统一起来,计算机表示的浮点数可以当成整数直接进行整数比较方式算法就完备了。仔细分析负整数的结构,把负整数对应的整数减去 -0 ,这样,浮点数对应的在计算机中表示的整数在整个区间就连续了。因此,所有的计算机表示用补码的负整数经过这次减法后,对应的整数也都是原码的负整数了,这样整个整数比较就变得连续了就相当于在整个浮点数范围内都是有效的了,只有被比较的浮点数连续或线性分布,将浮点数看成整数的比较算法才有意义。算法的关键是负数只需将最高位的1去掉,方法见程序的实现。注意,这里只是比较相等,不是比较大小,将被比较的两个64位浮点数之间允许有多少个可以精确表达的浮点数作为比较相等的关键,从而实现快速和高精度的算法。

最后的浮点数比较算法就是:

/* 函数: bool IsEqual(double f1, double f2, int precisionFloatNum) */

/* 功能:两个64位浮点数是否近似相等*/

/* 输入:两个64位浮点数f1, f2 */

/* precisionFloatNum 被比较的两个64位浮点数之间允许有多少个可以精确表达的浮点数 */

/* 输出: TRUE,两个浮点数64位近似相等;FALSE 两个64位浮点数不等 */

bool IsEqual(double f1, double f2, long int precisionFloatNum)

{

void *p1=&f1;

void *p2=&f2;

long long int bits1=*((long long int*)p1);

long long int bits2=*((long long int*)p2);

if((bits1>0&&bits2>0)||(bits1<0&&bits2<0))

{ bits1=(bits1>0)?bits1:(bits1-0x8000000000000000);

bits2=(bits2>0)?bits2:(bits2-0x8000000000000000);

return ((abs(bits1-bits2))< precisionFloatNum) ? TRUE : FALSE;

}

else

return FALSE;

}

通过编程实现浮点数相等的比较,相对误差比较的算法和将浮点数当在整数即线性化的比较的算法都能实现16位有效数字的精度,可实现最小到DBL_MIN数的比较。DBL_MIN的定义是:

#defineDBL_MIN

2.2250738585072014e-308

/*min positive value*/

在浮点数的绝对值大于DBL_MIN的基础上,所有浮点数相等的比较现在只取决于有效数的位数,由此完整实现了浮点数的相等比较。

3 结论

本文从浮点数在计算机内部表示的角度出发,介绍了双精度浮点数比较大小的优化算法,通过程序调试验证了算法的有效性,并提出了改进方法。对于两个数据的有效数字位数超过了16位(253)的浮点数的相等比较,只有提高数据表示的位数,才能实现,例如,长双精度浮点数有80位的数据宽度。从运算速度上看,将浮点数当成整数的浮点数线性化的算法在运算速度和精度上优于绝对和相对精度的算法,因为整数的减法和取绝对值运算速度比浮点数的运算要快。这里用LCC编译器实现的双精度的浮点数比较相等,其它编译环境的算法则需要一定的修改。至于单精度的浮点数和长双精度的浮点数,修改程序的相关部分即可实现。

[1] 杜叔强. 浅析C语言中的浮点数[J]. 兰州工业高等专科学校学报, 2010,17(5):27.

[2] David Goldberg. What every computer scientist should know about doubling-point Arithmetic [M]. Computing Surveys, 1991.

[3] 张宗杰,张明亮. C 语言中浮点数的存储格式及其有效数字位数[J].计算机与数字工程, 2006,34(1):85.

[4] http://blog.csdn.net/seizef/article/details/5571783.

[5] http://cmdblock.blog.51cto.com/415170/600378.

[6] 陈炜峰. 电磁脉冲模拟器及其应用研究[J]. 南京: 东南大学博士学位论文, 2007.

Approach to Compare to Equation of Double-precision Floating Point Numbers of Linearization in C Language

Zhang Guohua

(Wuhan Institute of Shipbuilding Technology, Wuhan430050, China)

Based on LCC software for design, this paper puts forward the design method of the double precision doubling- point comparisons algorithm, the accuracy of the algorithm is verified in applications, and the algorithm is designed with the absolute error and relative error method respectively. Finally aimed at representation for double-precision doubling-point number, the paper presents the arithmetic for floating point linearization.

compare; algorithms; double precision; significance digit

TP 312.1.4

A

1003-4862(2017)01-0040-03

2016-11-15

章国华(1964-),男,副教授。研究方向:机电一体化技术教学与研究。Email:993468391@qq.com

猜你喜欢
线性化位数整数
五次完全幂的少位数三进制展开
连续自然数及其乘积的位数分析
“线性化”在多元不等式证明与最值求解中的应用
基于反馈线性化的RLV气动控制一体化设计
一类整数递推数列的周期性
EHA反馈线性化最优滑模面双模糊滑模控制
空间机械臂锁紧机构等效线性化分析及验证
遥感卫星CCD相机量化位数的选择
叶丽娅的年龄
答案