精确乘法

时间:2013-09-25 08:50:32

标签: multiplication

第一篇文章!

我有一个程序的问题,我正在为数值模拟编写,我遇到了乘法问题。基本上,我试图计算:

result1 = (a + b)*c 

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

result2 = a*c + b*c

但是,当我这样做时,我的结果会出现重大错误。我使用了一个高精度的库,它确实改进了一些东西,但模拟速度非常慢(模拟时间长了50倍)并且它实际上不是一个实用的解决方案。由此我意识到它并不是变量a,b和&的精确度。这伤害了我,但是乘法的方式就是这样。

我的问题是:如何将这些括号相乘以使result1 = result2?

感谢。

解决!!!!!!!!!

这是添加的问题。所以我重新编写了这些术语,并通过编写以下代码来应用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分钟,而高精度库需要一天才能完成!!

感谢这里的所有帮助。这个问题在a $$中确实很痛苦。

3 个答案:

答案 0 :(得分:0)

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

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

想象一下,a和b都非常大,而且远小于1.(a + b)可能会溢出,因此result1将是不正确的。 result2不会溢出,因为它会在添加之前缩小所有内容。

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

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

答案 1 :(得分: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);
}

但是如果我将所有双打更改为float并使用c = 0.00001083394434,我确实发现了一个区别。你确定在进行计算时使用64(或80)位双打吗?

答案 2 :(得分:0)

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

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

一种方法是查看计算差异的地方,看看是否可以重新设计问题(例如,如果您可以区分功能,则可以将Y(x+dx)-Y(x)替换为dx * Y(x)'

关于数值稳定性的主题有许多优秀的参考文献。这是一个复杂的主题。只是“在这个问题上投入更多重要人物”几乎不是最佳解决方案。