符点数为什么不能精确存储,比如0.2计算机是不能精确存储的

来源:互联网 发布:网络骑士流星 编辑:程序博客网 时间:2024/06/05 17:22


转自:http://blog.csdn.net/ispeller/article/details/7643348


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>intmain (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>intmain (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,这都是可以直接比较的。

0 0
原创粉丝点击