float和double中精度数字的差异

时间:2017-10-23 15:34:07

标签: c floating-point

main()
{
    int z;
    float b=3.1;
    z=b*5+1.5;
    printf("%d",z);
}

我得到z的值为16。

当我使用双倍时,我得到17.为什么会这样?

2 个答案:

答案 0 :(得分:2)

有许多数字,如3.1 float/double无法正确编码 ,因为浮点典型使用二进制浮点编码。 ** 1在OP的情况下,不是在b中精确保存3.1,而是保存附近的值。作为floatdouble,使用了不同的近似值,导致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.13.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的不稳定而不是截断。