现在的位置: 首页 > 综合 > 正文

关于浮点数

2018年04月01日 ⁄ 综合 ⁄ 共 1769字 ⁄ 字号 评论关闭

一、浮点的精度限制

    看了看《深入理解计算机系统》中有关信息存储的内容,不得不感叹浮点数的存储真让人望而生畏。下面就看看编程中遇到浮点数需要注意的东西吧。

    浮点数的存储格式与整数完全不同。大部分的实现采用的是IEEE 754标准,float类型,是1个sign bit,8 exponent bits,23 mantissa bits。而double类型,是1个sign bit,11 exponent bits,52 mantissa bits。至于浮点如何去表示小数,请自行查找《深入理解计算机系统》中的相关章节。由于float使用的小数表示方法,导致浮点数值是有精度限制的。

    有限的精度就引发了浮点数值使用时的两个陷阱:

    1、交换定律不适用浮点数

    如有三个浮点数float x=1/3,y=1/6,z=1/7,而x*y/z不等于x*(y/z)。而对于整数来说,如果不发生溢出的情况下,x*y/z是等于x*(y/z)。

#include <stdlib.h>
#include <stdio.h>

int main(void)
{
    float x = 1/3;
    float y = 1/6;
    float z = 1/7;

    if (x*y/z != x*(y/z)) {
        printf("Not equal!\n");
    }

    return 0;
}

    2、浮点数的比较要使用范围比较

#include <stdlib.h>
#include <stdio.h>

int main(void)
{
    float x = 0.123-0.11-0.013;

    if (x == 0) {
        printf("x is 0!\n");
    }

    if (-0.0000000001 < x && x < 0.0000000001) {
        printf("x is in 0 range!\n");
    }

    return 0;
}

    这两个都是比较常见的浮点陷阱,下面要说明的是浮点数值的两个exception。
    (1)infinite无限
    (2)NaN即Not a Number
    其中NaN为最为特殊的一个“浮点值”——它不是一个合法的浮点值。请看下面的例子:

#include <stdlib.h>
#include <stdio.h>

int main(void)
{
    float x = 1/0.0;

    printf("x is %f\n", x);//infinite

    x = 0/0.0;

    printf("x is %f\n", x);//NaN

    return 0;
}

    当1除以0.0时,我们得到的是infinite,而是用0除以0.0时,得到的就是NaN。这里完全是普通的除法运算,也会产生NaN的情况。那么当使用除法的时候,对除数进行检查,保证其不为0.0是否就可以避免NaN了呢?再看下面的代码:

#include <stdlib.h>
#include <stdio.h>

int main(void)
{
    float x;

    while (1) {
        scanf("%f", &x);
        printf("x is %f\n", x);
    }

    return 0;
}

    示例代码中,调用scanf来得到用户输入的浮点数。令人惊讶的是,scanf作为C库函数是接受浮点数的这两种exceptions的,用户可以直接输入无限inf和NaN。而C库中究竟有多少种输入输出函数支持这两种exception不为所知。那么对于UI程序来说,当遇到浮点数值的时候,我们必须要判断该浮点数是否为一个合法的浮点数,要对用户输入值进行检查,或者说对于一切不属于本模块的浮点输入值都要进行检查。以上面的代码为例,应该为:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

int main()
{
    float x;

    while (1) {
        scanf("%f", &x);

        if (isinf(x)) {
            printf("It's infinite\n");
        }
        if (isnan(x)) {
            printf("It's NaN\n");
        }

        printf("x is %f, 0x%X\n", x, *(int*)&x);
    }

    return 0;
}

    其中isinf和isnan为C库提供的检测函数,分别用于检查infinite和NaN。而isnan实际上就是返回x != x,利用的就是NaN的特性,与任何数值进行相等比较都是返回false。所以当x != x时,即为NaN浮点值。

    好吧。就到这了,以后注意下就行咯。

抱歉!评论已关闭.