《编写高质量代码:改善c程序代码的125个建议》——建议4-2:浮点数转换为新类型时必须做范围检查

简介:

本节书摘来自华章计算机《编写高质量代码:改善c程序代码的125个建议》一书中的第1章,建议4-2,作者:马 伟 更多章节内容可以访问云栖社区“华章计算机”公众号查看。

建议4-2:浮点数转换为新类型时必须做范围检查

关于浮点类型数据的转换原则,在C99的6.3.1.4节与6.3.1.5节中做了非常重要的阐述,其表达的主要意思如下:
当我们将一个浮点类型的数据转换成除_Bool类型之外的一个整型数据时,该浮点数的小数部分须被丢弃,只保留它的整数部分。如果浮点数整数部分的值无法使用这种整型表示方法时,其行为是未定义的。
与此同时,如果我们将一个整数类型的数据转换成一个浮点类型时,如果该整型数据的值在该浮点数的取值范围内,并且能够被浮点类型精确表示,那么将会被正确转换;如果该整型数据的值在该浮点数的取值范围内,但不能够被浮点类型精确表示,那么转换的结果是最邻近的稍大或者稍小的可表示值;但如果该整型数据的值在该浮点数的取值范围外,其行为是未定义的。
当我们将一个double 类型降级转换为float类型、将long double类型降级转换到double或者float类型时,如果转换的值在新类型的取值范围内,并且能够被新类型精确表示,那么将会被正确转换;如果转换的值在新类型的取值范围内,但不能够被新类型精确表示,那么转换的结果是最邻近的稍大或者稍小的可表示值;但如果转换的值在新类型的取值范围外,其行为是未定义的。
由此可见,为了避免浮点数据转换时导致的未定义行为,我们应该在转换时对数据进行相关的范围检查。例如,下面的代码清单1-23演示了如何将double类型转换为int类型。

代码清单1-23 double转换为int类型示例
#include <stdio.h>
#include<limits.h>
int main(void)
{ 
    double d1=2147483648.01;
    int i1=0;
    if(d1>(double)INT_MAX||d1<(double)INT_MIN)
    {        
    }
    else
    {
            i1=(int)d1;
    }
    printf("i1=%d\n",i1);
    return 0;   
}

在上面的程序中,我们通过语句“if(d1>(double)INT_MAX||d1<(double)INT_MIN)”来对程序做类型转换时的取值范围检查,这样就可以避免在执行语句“i1=(int)d1”时发生未定义行为。
但需要特别强调的是,上面的程序是建立在double类型的取值范围大于int类型的取值范围的基础之上的。因此,在使用这种方法做取值范围检查时,你必须完全明白不同编译器所对应的相关类型的取值范围。假设在某个编译器中,double类型的取值范围小于int类型的取值范围,那么上面这种方法将是不可行的,实际上这种情况基本没有。
相对于浮点数与整数之间的转换,浮点数与浮点数之间的转换就简单多了。演示示例如代码清单1-24所示。

代码清单1-24  double与float类型转换示例
#include <stdio.h>
#include<limits.h>
#include<float.h>
int main(void)
{ 
    long double ld1=1.7976931348623158e+308;
    double d1=1.0;
    double d2=1.0;
    float f1=1.0f;
    float f2=1.0f;
    /*double->float*/
    if(d1>FLT_MAX||d1<FLT_MIN)
    {    
    }
    else
    {
            f1=(float)d1;
    }
    /*long double->double*/
    if(ld1>DBL_MAX||ld1<DBL_MIN)
    {
    }
    else
    {
            d2=(double)ld1;
    }
    /*long double->float*/
    if(ld1>FLT_MAX||ld1<FLT_MIN)
    {
    }
    else
    {
            f2=(float)ld1;
    }
    return 0;   
}
相关文章