-1

考虑以下 C++ 程序:

#include <iostream>
using std::cout;
using std::endl;

int main () {
   float x = 0.0001;
   float y = 0;
   for (int i=0; i < 10000; i++) {
      y += x;
   }
   cout << y << endl;
   return 0;
}

编译并运行该程序,然后回答以下问题: 该程序的实际行为与其预期行为有何不同?

为什么没有看到预期的行为?

在确保程序语义保持不变的同时,您将对该程序进行哪些更改以确保预期和实际行为匹配?

以上是我的任务。我知道我应该自己做作业,但我被困住了。

  • 对于 a) 部分,我只是说这两个数字不同。

  • 对于 c) 部分,我将浮点数设为双倍。(我认为语义保持不变)

  • 对于 b) 部分,我知道这被称为灾难性取消,但教授可能希望看到更多,我不知道还能说什么。有人能帮我吗?

谢谢你的帮助

4

2 回答 2

1

我想这个程序声称的“预期行为”是将 .0001 添加到初始化为零的总和 10,000 次,所有算术都是数学运算,产生 1。实际行为是将十进制数字“.0001”转换为双精度(可能是 IEEE-754 64 位二进制浮点数),然后将该值转换为浮点数(可能是 IEEE-754 32 位二进制浮点数),然后将该浮点数相加 10,000 次,使用每次浮点运算。因此,在将数字转换为双精度数、将双精度数转换为浮点数以及每次加法时,实际行为都存在潜在的舍入误差。

在这种情况下避免错误的一种方法是使用整数算术。我们可以设置为 1,而不是设置float x为 .0001 int x。类似地,y它将是一个 int,并且我们将使用所有整数运算,直到我们完成循环。获得最终总和后,我们会将其转换为浮点数。然后我们必须调整我们使用的允许我们使用整数算术的比例。由于我们添加的是 1 而不是 .0001,因此我们必须将最终结果除以10000.f进行调整。(这种技术不会在所有情况下都完全避免错误,还有其他技术可以减少其他情况下的错误。)

没有灾难性的取消,因为没有取消。当两个数相加或相减以产生较小的结果时,就会发生取消(因此,当将两个符号相反的数字相加时,例如将 +8 和 -6 相加得到 +2,或者将两个相同符号的数字相减时,例如相减-6 从 +8 到 +2)。当结果远小于原来的两个数字时,就会发生灾难性的取消。在这种情况下,我们正在使用的值变得更小,但原始数字中的任何错误通常保持相同的大小,因此相对而言,错误要大得多到我们正在使用的价值。例如,假设我们应该从 8.01 中减去 8 并得到 0.01,但是由于一个小错误,我们用 7.99 代替了 8。从 8.01 中减去 7.99 得到 0.02。这个结果 0.02 是期望结果 0.01 的两倍,因此相对误差很大。

于 2012-10-14T11:02:17.437 回答
1

该程序的实际行为与其预期行为有何不同?-该程序的实际行为是将 0.0001 的 IEEE 表示加 10000 次;IEEE 表示 0.0001 != 实际 0.0001

为什么没有看到预期的行为?- 我们假设 0.0001 准确地表示为 0.0001,但实际上并非如此,因为 IEEE 浮点数不能准确地表示 0.0001,因为必须用 base2 而不是 base10 表示所有浮点数。

在确保程序语义保持不变的同时,您将对该程序进行哪些更改以确保预期和实际行为匹配?- 在这种情况下,将 float 更改为 double 将起作用,因为 double 为您提供比 float 更高的小数精度。- 替代解决方案是保持浮动,而不是求和,而是分配 y = 10000*x(这会产生更少的错误,并且在您希望避免舍入和近似错误时更好)

于 2012-10-14T04:31:23.180 回答