您的位置:首页 > 其它

为什么两个不确定值的浮点数无法直接比较是否相等

2012-06-07 21:48 309 查看
C语言贴吧看到的:



首先,为了让代码清楚一点,把楼主耍小聪明的伎俩去掉:



程序运行的结果是执行了if (a != a) 语句块的内容。

a = a / a 没什么好奇怪的,关于执行的结果我开始是这么认为的:
a = a / a 完了之后a 的值是NaN,表示不是任何数(后来我发现,对于大多数环境来说,这个没有定死)。
NaN 的32 位精度储存是这样的:
符号位:可以为0 或者1
指数域:0xFF
尾数域:Non zero
而IEEE 754明确规定(摘自维基百科):
比较运算. IEEE754定义了特殊情况: -inf = -inf, inf = inf and x ≠ NaN for any x (including NaN).
这样NaN 自然不等于NaN。

我把这个问题放到了群里:



群里的楼兰君如是说,我又很相信楼兰君,就到处查了一下:
程序执行了if (a != 1) 语句块的内容,不是完全和我想的一样 NaN != NaN
因为计算机没法对浮点型数进行精确的储存,所以对不确定值浮点数使用 == 和 != 运算符都是根本性错误的。
比较一个不确定浮点数有一种办法,就是做减法之后和某一个足够小的值取绝对值做大小比较,如果小于该值,那么就可以认为两个浮点数是相等的。
例如
if (fabs (floatNumber1 - floatNumber2) < 0.0000001) {
/* other code */

}

为什么这里说“不确定的浮点数”呢?你可以运行下面的代码:

#include <stdio.h>

int
main (void) {

printf ("0.2+0.1-0.1");
if (0.2+0.1-0.1 < 0.2) {
printf (" < ");
} else if (0.2+0.1-0.1 > 0.2) {
printf (" > ");
} else {
printf (" == ");
}
printf ("0.2");

getchar ();
return 0;
}


我在Windows 7 用gcc 编译的结果:



然后你再试试这段代码少年,只是修改了一些数值而已:

#include <stdio.h>

int
main (void) {

printf ("0.5+0.125-0.125");
if (0.5+0.125-0.125 < 0.5) {
printf (" < ");
} else if (0.5+0.125-0.125 > 0.5) {
printf (" > ");
} else {
printf (" == ");
}
printf ("0.5");

getchar ();
return 0;
}


相同的环境,我的结果是:



为什么都是看起来相同的数值,两段程序的结果就是不一样呢?
我们日常使用的是十进制,而计算机内部使用了二进制,十进制用二进制表示的时候,向上取整是一个不可避免的问题,我们看到的0.1 和 0.2 实际上是被向上取整的结果,而0.125 和 0.5 则是确定的数值,不会向上取整,所以输出了等于。

让我们看看具体的情况:
一个浮点数 根据IEEE 754 标准,是如下图存放的(约定俗成右边为低位):
32位的情况:



64位的情况:



NND昨天晚上突然断网了,垃圾学校垃圾网络。
昨晚打了一晚上雷,真害怕我的本本被劈死,幸好学校的网络没有悲剧。
接着说,这里只说32位的:
符号位复位代表正,置位就是负。
指数域是用移码表示的,这里会有一个127的偏移量,例如指数是2,那么指数域的值就是127+2 = 129。指数是-1,指数域的值就是127-1 = 126。
尾数域全是小数点之后的数,但是默认省略了一个最高位的1,如果尾数位全是0,其实是 1 0 0 0 ... 0
下面用乘2取整把 0.125 换成浮点数表示:
A. 0.125 x 2 = 0.25,取整数部分0,小数部分为0.25
B. 0.25 x 2 = 0.5,取整数部分0,小数部分为0.5
C. 0.5 x 2 = 1.0,取整数部分1,小数部分为0,结束
D. 从第一位开始读数,直到最后一位,得出 0.5 的二进制是0.001b
这时候就可以按照上面的浮点数结构把0.0001b 写成浮点数了,这里有个重要的问题:0.5 写成二进制是确定的值,相应浮点数也是确定的。

为什么这么说呢,你可以用上面的方法把 0.2 化成二进制格式,你会发现一个问题:小数部分无法取到0,算法无法停止,而浮点数的位是有限的,那么只能保存一个近似的数值。这里的方法和十进制的四舍五入差不多,这里是零舍一入。既然0.2 是有取整情况存在的,那么0.1 也必然是要取整的。0.5 只需要用脚趾头想一下就知道,这玩意是可以确定的。
第一个程序的两个浮点数全部都是无法确定的,只能给出近似值,而第二个程序的两个浮点数全部是可以确定的,当然会得到如图片所示的结果。
所以和楼兰说的一样,对不确定值的浮点数直接比较是否相等根本就是错误的

当然不是说楼主的代码有问题,目前已知的环境, a /= a 的结果有两种:NaN == a 或者 0.0 == a,这都是可以直接比较的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: