我在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输出匹配,即正确总和的八分之一,而不是一半。
答案 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)
浮点数表示任意两个数字之间的所有无限可能值;但是,计算机无法容纳无限数量的值。因此妥协了。浮点数保持值的近似值。
这意味着如果您选择的值比存储的浮点数“更多”,但不足以达到“下一个”可存储的近似值,那么存储逻辑上更大的数字实际上不会改变浮点数值。
浮点近似中的“误差”是可变的。对于小数字,误差更精确;对于更大的数字,误差按比例相同,但实际值更大。