了解可以存储在C

时间:2017-10-12 20:49:59

标签: c floating-point

我在C中遇到了一些我不理解的float类型的行为,希望可以解释一下。使用float.h中定义的宏,我可以确定数据类型可以在给定硬件上存储的最大/最小值。但是,当执行不应超过这些限制的计算时,我发现类型float变量在double成功的地方失败。 以下是一个最小的例子,它在我的机器上编译。

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

int main(int argc, char **argv)
{
    int gridsize;
    long gridsize3;

    float *datagrid;

    float sumval_f;
    double sumval_d;

    long i;

    gridsize = 512;
    gridsize3 = (long)gridsize*gridsize*gridsize;

    datagrid = calloc(gridsize3, sizeof(float));
    if(datagrid == NULL)
    {
        free(datagrid);
        printf("Memory allocation failed\n");
        exit(0);
    }

    for(i=0; i<gridsize3; i++)
    {
        datagrid[i] += 1.0;
    }

    sumval_f = 0.0;
    sumval_d = 0.0;
    for(i=0; i<gridsize3; i++)
    {
        sumval_f += datagrid[i];
        sumval_d += (double)datagrid[i];
    }

    printf("\ngridsize3 = %e\n", (float)gridsize3);
    printf("FLT_MIN = %e\n", FLT_MIN);
    printf("FLT_MAX = %e\n", FLT_MAX);
    printf("DBL_MIN = %e\n", DBL_MIN);
    printf("DBL_MAX = %e\n", DBL_MAX);

    printf("\nfloat sum = %f\n", sumval_f);
    printf("double sum = %lf\n", sumval_d);
    printf("sumval_d/sumval_f = %f\n\n", sumval_d/(double)sumval_f);

    free(datagrid);
    return(0);
}

使用gcc进行编译我找到了输出:

gridsize3 = 1.342177e+08
FLT_MIN = 1.175494e-38
FLT_MAX = 3.402823e+38
DBL_MIN = 2.225074e-308
DBL_MAX = 1.797693e+308

float sum = 16777216.000000
double sum = 134217728.000000
sumval_d/sumval_f = 8.000000

虽然使用icc编译sumval_f = 67108864.0,因此最终比率为2.0 *。请注意,float总和不正确,而double总和是正确的。

据我所知,FLT_MAX的输出表明总和应该适合float,但它似乎在全值的八分之一或一半处达到稳定水平。

是否存在使用float.h找到的值的编译器特定覆盖? 为什么double需要正确找到此数组的总和?

*有趣的是,在打印数组值的for循环中包含if语句会导致该值与gcc输出匹配,即正确总和的八分之一,而不是一半。

3 个答案:

答案 0 :(得分:5)

这里的问题不是值的范围,而是精度

假设32位IEEE754 float,此数据类型最多具有24位精度。这意味着并非所有大于16777216的整数都可以准确表示。

因此,当您的总和达到16777216时,向它添加1超出了数据类型可以存储的精度,因此该数字不会变得更大。

A(大概)64位double具有53位精度。这足以容纳所有整数值,最大值为134217728,因此它可以为您提供准确的结果。

答案 1 :(得分:2)

float可以精确地表示介于-16777215和+16777215之间的任何整数。它还可以表示-2 * 16777215和+ 2 * 16777215之间的所有偶数整数(包括+/- 2 * 8388608,即16777216),所有4的倍数在-4 * 16777215和+ 4 * 16777215之间,同样适用于所有功率两个缩放因子高达2 ^ 104(大约2.028E + 31)。此外,它可以表示从-16777215/2到+16777215/2的1/2的倍数,从-16777215/4到+16777215/4等的1/4的倍数,再到-1 / 2 ^ 149的倍数 - 167777215 /(2 ^ 149)至+ 16777215 /(2 ^ 149)。

答案 2 :(得分:1)

浮点数表示任意两个数字之间的所有无限可能值;但是,计算机无法容纳无限数量的值。因此妥协了。浮点数保持值的近似值。

这意味着如果您选择的值比存储的浮点数“更多”,但不足以达到“下一个”可存储的近似值,那么存储逻辑上更大的数字实际上不会改变浮点数值。

浮点近似中的“误差”是可变的。对于小数字,误差更精确;对于更大的数字,误差按比例相同,但实际值更大。