使用float而不是double时输出奇怪

时间:2015-02-14 05:11:15

标签: c floating-point floating-point-precision

当我使用float代替double

时输出奇怪
#include <stdio.h>
void main()
{
    double p,p1,cost,cost1=30;
    for (p = 0.1; p < 10;p=p+0.1)
    {
        cost = 30-6*p+p*p;
        if (cost<cost1)
        {
            cost1=cost;
            p1=p;
        }
        else
        {
            break;
        }
        printf("%lf\t%lf\n",p,cost);
    }
    printf("%lf\t%lf\n",p1,cost1);
}

As Expected

p = 3;

时按预期提供输出

但是当我使用float时,输出有点奇怪。

#include <stdio.h>
void main()
{
    float p,p1,cost,cost1=40;
    for (p = 0.1; p < 10;p=p+0.1)
    {
        cost = 30-6*p+p*p;
        if (cost<cost1)
        {
            cost1=cost;
            p1=p;
        }
        else
        {
            break;
        }
        printf("%f\t%f\n",p,cost);
    }
    printf("%f\t%f\n",p1,cost1);
}

Strange increment style

为什么第二种情况下p的增量在2.7之后变得奇怪?

2 个答案:

答案 0 :(得分:11)

这种情况正在发生,因为floatdouble数据类型在基数2中存储数字。大多数基数为10的数字无法准确存储。使用float时,舍入错误会更快地加起来。在内存有限的嵌入式应用程序之外,出于这个原因,使用double通常会更好,或者至少更容易。

要查看double类型的发生情况,请考虑此代码的输出:

#include <stdio.h>
int main(void)
{
    double d = 0.0;
    for (int i = 0; i < 100000000; i++)
        d += 0.1;
    printf("%f\n", d);
    return 0;
}

在我的电脑上输出9999999.981129。因此,经过1亿次迭代后,舍入误差在结果中产生了0.018871的差异。

有关浮点数据类型如何工作的更多信息,请阅读What Every Computer Scientist Should Know About Floating-Point Arithmetic。或者,正如评论中提到的akira,请参阅Floating-Point Guide

答案 1 :(得分:5)

您的程序可以使用float正常运行。您不需要double来计算包含100个值到几位有效数字的表。您可以使用double,如果这样做,即使您在交叉目的使用二进制浮点二进制文件,它也有机会工作。大多数C编译器用于double的IEEE 754双精度格式是如此精确,以至于它使得浮点的许多误用都不明显(但不是全部)。

十进制简单的值在二进制

中可能不简单

结果是十进制简单的值可能无法用二进制表示。

这是0.1的情况:它在二进制中并不简单,并且它没有完全表示为doublefloat,但double表示具有更多的数字和结果,更接近预期值1/10。

浮点运算一般不精确

floatdouble等格式的二进制浮点运算必须以预期的格式生成结果。这导致每次计算操作时必须从结果中删除一些数字。当以高级方式使用二进制浮点时,程序员有时会知道结果将具有足够的数字,以便以格式表示所有数字(换句话说,有时浮点运算可以是精确的和高级程序员可以预测并利用发生这种情况的条件)。但是在这里,你要添加0.1,这不简单,并且(二进制)使用所有可用的数字,因此大多数情况下,这个添加并不准确。

如何仅使用float

打印一个小的值表

for (p = 0.1; p < 10;p=p+0.1)中,p的值float将在每次迭代时舍入。每次迭代都将从之前已经舍入的迭代计算出来,因此舍入误差将累积并使最终结果偏离预期的数学值。

以下列出了对所写内容的改进,完全相反:

for (i = 1, p = 0.1f; i < 100; i++, p = i * 0.1f)

在上面的版本中,0.1f并不完全是1/10,但p的计算只涉及一次乘法和一次舍入,而不是最多100次。该版本给出了更精确的近似值i / 10。

for (i = 1, p = 0.1f; i < 100; i++, p = i * 0.1)

在上面略有不同的版本中,i乘以double0.1,后者更接近1/10。结果总是最接近float到i / 10,但这个解决方案有点作弊,因为它使用double乘法。我说只有float的解决方案!

for (i = 1, p = 0.1f; i < 100; i++, p = i / 10.0f)

在最后一个解决方案中,p被计算为i的除法,完全表示为float,因为它是一个小整数,10.0f,也是出于同样的原因。唯一的计算近似是单个操作,,参数正是我们想要的,所以这是最好的解决方案。对于1到99之间的所有i值,它产生最接近的float到i / 10.