简体   繁体   English

为什么下面代码的output不为零?

[英]Why is the output of the following code not zero?

{
    double A = 373737.0;
    double B;

    B = A*A*A + 0.37/A - A*A*A - 0.37/A;
    printf("The value of B is %f.\n", B);
}

In this C code, why is the value of B not zero, but instead -0.000001?在这个 C 代码中,为什么 B 的值不是零,而是 -0.000001?

Several people have mentioned in comments that neither 0.37 nor 0.37/373737.0 can be represented accurately in binary floating point, which is true.有几个人在评论中提到 0.37 和 0.37/373737.0 都不能用二进制浮点数准确表示,这是事实。 But that's a bit of a red herring.但这有点转移注意力。 You could replace 0.37/A with 1 , which is clearly representable accurately in floating point, and you'd still get an odd result.您可以将0.37/A替换为1 ,这在浮点数中可以清楚地准确表示,但您仍然会得到一个奇怪的结果。 Perhaps that odd result would be more revealing.也许那个奇怪的结果会更能说明问题。 It's certainly worth trying the experiment yourself.亲自尝试这个实验当然是值得的。

Floating point arithmetic might turn out differently on different machines (although these days, that's uncommon) but under most circumstances, in a single program run on a single machine, 0.37/373737.0 will always be the same value.浮点运算在不同的机器上可能会有不同的结果(尽管现在这种情况并不常见),但在大多数情况下,在一台机器上运行的单个程序中,0.37/373737.0 将始终是相同的值。 So you might expect that the two instances of 0.37/A will cancel out.所以您可能期望0.37/A的两个实例会抵消。

And they would have, had you written A*A*A - A*A*A + 0.37/A - 0.37/A .如果你写的是A*A*A - A*A*A + 0.37/A - 0.37/A ,他们就会有。 But that's not the order you did the operations, and the result is the round-off error that you experienced.但这不是您执行操作的顺序,结果是您遇到的舍入错误。 The problem is that A*A*A is quite a large number, well outside the range of integers which can be represented accurately in a 64-bit double.问题是A*A*A是一个很大的数字,远远超出了可以用 64 位双精度精度表示的整数范围。 And moreover, 0.37/A is a small number.而且, 0.37/A是一个很小的数字。 Consequently, A*A*A + 0.37/A is exactly the same as A*A*A ;因此, A*A*A + 0.37/AA*A*A完全相同; the exact sum is quite a bit smaller than the next larger double-precision value.确切的总和比下一个更大的双精度值小很多。

Thus, A*A*A + 0.37/A - A*A*A is 0;因此, A*A*A + 0.37/A - A*A*A为 0; the 0.37/A simply vanishes. 0.37/A就消失了。 And then A*A*A + 0.37/A - A*A*A - 0.37/A is -0.37/A , which is approximately 0.000001 (if you printed it with more precision, you'd see a more accurate output).然后A*A*A + 0.37/A - A*A*A - 0.37/A-0.37/A ,大约是 0.000001 (如果你更精确地打印它,你会看到更准确的输出)。

Standard C does not allow the compiler to regroup floating point computations, because it would make it impossible to predict the result.标准 C 不允许编译器重新组合浮点计算,因为这会导致无法预测结果。 If you need a particular order of evaluation to avoid numeric instability, you have to do it yourself.如果您需要特定的评估顺序以避免数值不稳定,则必须自己完成。 (GCC does provide the -funsafe-math-optimizations flag, which can violate the C standard, produce unpredictable results, and sometimes accidentally produce the result you expected. Don't use it. It's unsafe.) (GCC 确实提供了-funsafe-math-optimizations标志,它会违反 C 标准,产生不可预测的结果,有时会意外地产生你预期的结果。不要使用它,它是不安全的。)

And part of learning how to write numerical programs is learning how to reorder your computations to avoid this type of error.学习如何编写数值程序的一部分是学习如何重新排序计算以避免此类错误。 One rule is, never subtract two large but similar numbers, since that will produce a wildly inaccurate result.一个规则是,永远不要减去两个大但相似的数字,因为这会产生非常不准确的结果。 (That's the rule which your expression breaks.) (这是你的表达打破的规则。)

Any good text book on numerical computation should go into much more detail about these issues.任何关于数值计算的好教科书都应该 go 对这些问题进行更详细的介绍。 You could also start with the venerable essay, already mentioned in comments,What every computer scientist should know about floating point arithmetic .您也可以从评论中已经提到的著名文章开始,每位计算机科学家都应该了解浮点运算

If your math gives irrational or repeating numbers after one or more opraters is done, the computer will cut it off or rounded, therefor, it's not 0. This is called round off error .如果你的数学在一个或多个运算完成后给出无理数或重复数,计算机会将其截断或四舍五入,因此它不是 0。这称为舍入误差

There are many reason may be you expression opration become out of range of double variable.You are printing double value using float format pacifier.Resolve this may it will give you desired output.有很多原因可能是你的表达式操作超出了双精度变量的范围。你正在使用浮点格式的奶嘴打印双精度值。解决这个问题可能会给你想要的 output。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM