C中的浮点舍入
我用浮动遇到了一些奇怪的舍入行为。 下面的代码演示了这个问题。 解决这个问题的最佳方法是什么? 我一直在寻找解决方案,但没有太多运气。
#include int main(void) { float t; t = 5592411; printf("%f\n", 1.5*t); t *= 1.5; printf("%f\n", t); return 0; }
上面的代码应打印出相同的值,但我使用GCC 4.7.2在我的设置上得到了这个:
8388616.500000
8388616.000000
如果我使用计算器,我得到第一个值,所以我假设第二个值是以某种方式舍入。 我有相同的Fortran代码,它不会对值进行舍入(具有0.5)。
1.5
是double
常数而不是float
,C具有自动升级规则。 所以当你执行1.5*t
时会发生什么(i) t
被转换为double
; (ii) double
乘以double
1.5
; (iii)打印double
精度( %f
是double
精度的格式化程序)。
相反, t *= 1.5
将t
提升为double,执行双倍乘法,然后截断结果以将其存储回[单精度] float
。
有关证据,请尝试:
float t; t = 5592411; printf("%f\n", 1.5f*t); // multiply a float by a float, for no promotion t *= 1.5; printf("%f\n", t); return 0;
要么:
double t; // store our intermediate results in a double t = 5592411; printf("%f\n", 1.5f*t); t *= 1.5; printf("%f\n", t); return 0;
第一次计算是使用双精度完成的,第二次计算相同,但在赋值为float
被截断为单精度。
如果对变量使用double
,则会得到相同的结果。 当精度可能成为问题时,最好在float
上使用这种类型。
在第一种情况下,结果是双精度,它可以精确地表示所需的值。
在第二种情况下,结果是一个浮点数,它不能精确地表示所需的值。
尝试使用double,你最终会得到相同的结果。
#include int main(void) { double t; t = 5592411; printf("%f\n", 1.5*t); t *= 1.5; printf("%f\n", t); return 0; }
在C代码中编写1.5被解释为double,其精度高于float类型。
第一种情况,
printf("%f\n", 1.5*t);
导致t被隐式转换为double(精度更高)然后相乘。 printf
函数无论如何都会输出对应于%f
的输入,打印结果,这也是一个double
。
第二种情况是将1.5转换为float
类型,其精度较低,无法存储为小细节。
如果要避免此效果,请使用1.5f
而不是1.5
来使用floats
,或将t
的类型更改为double
。
这是否可行取决于浮动和双打的机器表示。 在典型的32位体系结构上传递浮点数会在参数堆栈上推送4个字节。 传递一个双字符将推送8个字节。 传递一个double但是使用%f要求将它视为一个浮点数,它将查看在我们典型情况下推送的前4个字节。 根据机器表示,这可能接近预期结果,或者可能在左侧字段中显示出来。