0

第一篇文章!

我为数值模拟编写的程序有问题,乘法有问题。基本上,我试图计算:

result1 = (a + b)*c 

这会循环数千次。我需要将此代码扩展为

result2 = a*c + b*c

但是,当我这样做时,我的结果开始出现重大错误。我使用了一个高精度库,它确实改进了一些东西,但是模拟运行速度非常慢(模拟花费了 50 倍的时间),而且它确实不是一个实用的解决方案。由此我意识到,真正伤害我的并不是变量 a、b 和 c 的精度,而是乘法完成的方式。

我的问题是:我怎样才能将这些括号乘以结果1 = 结果2?

谢谢。

解决了!!!!!!!!!

这是添加的问题。因此,我通过编写以下代码对术语进行了重新排序并应用了 Kahan 加法:

double Modelsimple::sum(double a, double b, double c, double d) {
    //reorder the variables in order from smallest to greatest
    double tempone = (a<b?a:b);
    double temptwo = (c<d?c:d);
    double tempthree = (a>b?a:b);
    double tempfour = (c>d?c:d);
    double one = (tempone<temptwo?tempone:temptwo);
    double four = (tempthree>tempfour?tempthree:tempfour);
    double tempfive = (tempone>temptwo?tempone:temptwo);
    double tempsix = (tempthree<tempfour?tempthree:tempfour);
    double two = (tempfive<tempsix?tempfive:tempsix);
    double three = (tempfive>tempsix?tempfive:tempsix);
    //kahan addition
    double total = one;
    double tempsum = one + two;
    double error = (tempsum - one) - two;
    total = tempsum;
    // first iteration complete
    double tempadd = three - error;
    tempsum = total + tempadd;
    error = (tempsum - total) - tempadd;
    total = tempsum;
    //second iteration complete
    tempadd = four - error;
    total += tempadd;
    return total;
}

这给了我与精确答案一样接近的结果,没有区别。然而,在一个矿井坍塌的虚构模拟中,添加 Kahan 的代码需要 2 分钟,而高精度库需要一天才能完成!

感谢这里的所有帮助。这个问题真是让人头疼。

4

3 回答 3

0

通常,这类计算中的“精度损失”可以追溯到“公式化问题”。例如,当您必须添加一系列大小非常不同的数字时,您将得到不同的答案,具体取决于您对它们求和的顺序。当你减去数字时,问题就更加严重了。

在上述情况下,最好的方法是不仅查看这一行,还result1查看后续计算中使用的方式。原则上,工程计算不应要求最终结果的精度超过大约三位有效数字;但在许多情况下(例如,有限元方法),您最终会减去两个大小非常相似的数字 - 在这种情况下,您可能会丢失许多有效数字并得到毫无意义的答案。鉴于您在谈论“材料特性”和“应变”,我怀疑这实际上是您问题的核心。

一种方法是查看您计算差异的地方,看看您是否可以重新表述您的问题(例如,如果您可以区分您的函数,您可以替换Y(x+dx)-Y(x)dx * Y(x)'.

关于数值稳定性的主题有很多优秀的参考资料。这是一个复杂的主题。仅仅“在问题上抛出更重要的数字”几乎从来都不是最好的解决方案。

于 2013-09-30T15:13:43.690 回答
0

我假设您的数字都是浮点值。

由于数字规模和计算精度的限制,您不应期望 result1 等于 result2。使用哪一个将取决于您处理的数字。比 result1 和 result2 相同更重要的是它们足够接近您的应用程序的真实答案(例如,您将手动计算)。

假设 a 和 b 都非常大,而 c 远小于 1。 (a + b) 可能会溢出,因此 result1 将不正确。result2 不会溢出,因为它在添加之前会缩小所有内容。

在组合大小差异很大的数字时也会出现精度损失的问题,因为当较小的数字转换为使用与添加的较大数字相同的指数时,有效数字会减少。

如果您给出一些导致您出现问题的 a、b 和 c 的具体示例,则可能会提出进一步的改进建议。

于 2013-09-25T09:05:31.663 回答
0

我一直在使用以下程序作为测试,使用 a 和 b 的值在 10^5 和 10^10 之间,c 在 10^-5 左右,但到目前为止找不到任何差异。

考虑 10^5 和 10^10 的存储,我认为它需要大约 13 位和 33 位,因此在 result1 中将 a 和 b 加在一起可能会损失大约 20 位的精度。

但是将它们乘以相同的值 c 本质上会减少指数,但保留有效位相同,因此它也应该在 result2 中损失大约 20 位的精度。

双有效位通常存储 53 位,因此我怀疑您的结果仍将保留 33 位,或大约 10 位十进制数字的精度。

#include <stdio.h>

int main()
{
  double a = 13584.9484893449;
  double b = 43719848748.3911;
  double c = 0.00001483394434;

  double result1 = (a+b)*c;
  double result2 = a*c + b*c;

  double diff = result1 - result2;

  printf("size of double is %d\n", sizeof(double));
  printf("a=%f\nb=%f\nc=%f\nr1=%f\nr2=%f\ndiff=%f\n",a,b,c,result1,result2,diff);
}

但是,如果我将所有双打更改为浮动并使用 c=0.00001083394434,我确实会发现不同之处。您确定在进行计算时使用的是 64(或 80)位双精度数吗?

于 2013-09-25T10:13:11.623 回答