main()
{
int z;
float b=3.1;
z=b*5+1.5;
printf("%d",z);
}
我得到z的值为16。
当我使用双倍时,我得到17.为什么会这样?
答案 0 :(得分:2)
有许多数字,如3.1 float/double
无法正确编码 ,因为浮点典型使用二进制浮点编码。 ** 1在OP的情况下,不是在b
中精确保存3.1,而是保存附近的值。作为float
和double
,使用了不同的近似值,导致16对17。
FP数学和int
截断的结果突出了这种差异。
#include <float.h>
#include <stdio.h>
int main(void) {
printf("%d\n",FLT_EVAL_METHOD);
float b=3.1;
printf("%.20f %.20f %d\n",b, b*5+1.5, (int) (b*5+1.5));
volatile float v=3.1;
printf("%.20f %.20f %d\n",v, v*5+1.5, (int) (v*5+1.5));
double d=3.1;
printf("%.20f %.20f %d\n",d, d*5+1.5, (int) (d*5+1.5));
}
输出
2
3.09999990463256835938 16.99999952316284179688 16
3.09999990463256835938 16.99999952316284179688 16
3.10000000000000008882 17.00000000000000000000 17
有时FLT_EVAL_METHOD
的效果允许将某些float
数学作为double
数学来完成,这会使故事复杂化。因此,如果其他人尝试使用此代码的变体,上面的内容会使用volatile float
。
提示:如果有足够的警告,代码可能会收到如下警告。这暗示3.1
和3.1f
不一样。
// warning: conversion to 'float' alters 'double' constant value [-Wfloat-conversion]
float b=3.1;
// no warning
float b=3.1f;
作为直接为int
分配FP值的替代方法,请考虑各种舍入函数,例如round(), roundf(), nearby(), rint(), lrint()
等。
#include <math.h>
// example: float to long
long int lrintf(float x);
long long_var = lrintf(float_var);
** 1有无限数量的数字。然而,只有大约2 32 不同float
。
答案 1 :(得分:1)
3.1不能精确表示为二进制浮点数,就像3⅓不能用十进制精确表示。因此,如果您更改b
的类型,那么float
中存储的最接近的值可能是double
(或b
)。这可能会略微减少或稍微多一些,具体取决于需要截断分数的位置。
如果结果稍微多一点,5*b+1.5
会稍微多一点。超过17;否则会略微减少。但转换为int
会抛弃分数,所以这个微小的差异突然变得很重要。
你应该避免rounding的不稳定而不是截断。