c中的浮点不准确性

时间:2016-10-21 03:48:50

标签: c floating-point

我知道浮点数值可以准确表达的数字是有限的,我发现很多网站都描述了为什么会发生这种情况。但我还没有找到有关如何有效处理这个问题的任何信息。但是我确定NASA不能用0.2 / 0.1 = 0.199999。例如:

#include <stdio.h>
#include <float.h> 
#include <math.h>

int main(void)
{
    float number = 4.20;
    float denominator = 0.25;

    printf("number = %f\n", number);
    printf("denominator = %f\n", denominator);
    printf("quotient as a float = %f should be 16.8\n", number/denominator);
    printf("the remainder of 4.20 / 0.25 = %f\n", number - ((int) number/denominator)*denominator);
    printf("now if i divide 0.20000 by 0.1 i get %f not 2\n", ( number - ((int) number/denominator)*denominator)/0.1);
}

输出:

number = 4.200000
denominator = 0.250000
quotient as a float = 16.799999 should be 16.8
the remainder of 4.20 / 0.25 = 0.200000
now if i divide 0.20000 by 0.1 i get 1.999998 not 2

那么我如何使用浮点数(或小数或双精度数)进行算术运算并获得准确的结果。希望我没有错过一些非常明显的东西。任何帮助都是极好的!感谢。

3 个答案:

答案 0 :(得分:3)

解决方案是不要将浮点数用于不能接受舍入错误的应用程序。使用像GNU MP Bignum这样的扩展精度库(a.k.a.任意精度库)。有关任意精度库的精彩列表,请参阅this Wikipedia page。有关详细信息,另请参阅有关rational data typesthis thread的维基百科文章。

如果您要使用浮点表示(floatdouble等),则使用可接受的方法编写代码以处理舍入错误(例如,避免==)。有很多关于如何做到这一点的在线文献,这些方法根据所涉及的应用和算法而有很大差异。

答案 1 :(得分:1)

大部分时间浮点都很好。以下是我要记住的关键事项:

  • floatdouble之间存在很大差异。 double在大多数情况下为大多数事物提供足够的精确度; float令人惊讶地经常给你不够。除非你知道自己在做什么,并且有充分的理由,否则只需使用double

  • 有一些浮点不好的东西。虽然C本身不支持它,但定点通常是一个不错的选择。如果你用美分而不是美元进行财务计算,你实际上是使用固定点 - 也就是说,如果你使用代表便士的intlong int,并记得给小数点两点当它打印出来作为美元时,从右边的位置。

  • 您使用的算法真的很重要。朴素或“明显”的算法很容易最终放大舍入误差的影响,而更复杂的算法可以最小化它们。一个简单的例子是你添加浮点数的顺序很重要。

  • 从不担心16.8对16.799999。这种事总是会发生,但这不是问题,除非你把它变成一个问题。如果你想要一个小数点后的地方,只需用%.1f打印它,printf会为你舍入它。 (也不要试图比较浮点数以求完全相等,但我认为你现在已经听过了。)

  • 与上述相关,请记住0.1不能完全用二进制表示(正好1/3不能用十进制表示)。这只是众多原因中的一个,即使它们完全正常并且不会引起问题,你总会得到看起来很小的“错误”。

  • 偶尔你需要一个多精度(MP或“bignum”)库,它可以表示任意精度的数字,但这些(相对)缓慢且(相对)使用繁琐,幸运的是你通常不会需要它们。但是知道它们的存在是很好的,如果你是一个数学,那么使用它们会很有趣。

  • 有时,用于表示有理数的库很有用。例如,这样的库代表数字1/3作为数字对(1,3),因此在尝试将该数字表示为0.333333333时没有固有的不准确性。

其他人已经推荐了论文每个计算机科学家应该知道的关于浮点运算的内容,这是非常好的, 标准参考,虽然它很长而且相当技术性。我可以推荐的更容易和更短的阅读是来自我曾经教过的课程的讲义:https://www.eskimo.com/~scs/cclass/handouts/sciprog.html#precision。这个有点过时了,但它应该让你开始基础。

答案 2 :(得分:-1)

这不是一个好的答案,而且往往是一个问题。

如果数据是完整的,例如以美分计算的金额,然后将其存储为整数,这可能意味着双倍被限制为持有整数美分而不是合理数量的美元。但这只会在一些情况下有所帮助。

作为一般规则,当您尝试除以接近零的数字时,会出现不准确的情况。因此,您只需编写算法即可避免或抑制此类操作。有很多关于&#34;数字稳定的讨论&#34;与#34;不稳定&#34;算法,这是一个太大的主题,在这里公平。然后,通常,最好将浮点数视为具有较小的随机误差。如果它们最终代表现实世界中模拟值的测量值,则无论如何都必须存在一定的容差或不准确性。

如果您在做数学而不是处理数据,那么就不要使用C或C ++。使用符号代数包,例如Maple,它将诸如sqrt(2)之类的值存储为表达式而不是浮点数,因此sqrt(2)* sqrt(2)将始终精确地给出2,而不是非常接近的数字到2.