添加一个浮点数数组会产生奇怪的总和,正向与反向相加

时间:2019-06-19 13:38:20

标签: perl floating-point

我在perl中添加(求和)浮点数和数组,并且我试图使其加快速度。当我尝试时,我开始得到奇怪的结果。

#!/usr/bin/perl

my $total = 0;
my $sum = 0;

# Compute $sum (adds from index 0 forward)
my @y = @{$$self{"closing"}}[-$periods..-1];
my @x = map {${$_}{$what}} @y;
# map { $sum += $_ } @x;
$sum += $_ for @x;

# Compute $total (adds from index -1 backward)

for (my $i = -1; $i >= -$periods; $i--) {
    $total += ${${$$self{"closing"}}[$i]}{$what};
}

if($total != $sum) {
    printf("SMA($what, $periods) total ($total) != sum ($sum) %g\n",
       ($total - $sum));
}

# Example output:
#    SMA(close, 20) total (941.03) != sum (941.03) -2.27374e-13

计算$sum$total时似乎得到了不同的答案。

我唯一能想到的是,一个方法在数组中向前添加,而另一个方法向后添加。

这会导致它们以不同方式溢出吗?我期望如此,但是我从未想到会得到不同的答案。请注意,差异很小(-2.27374e-13)。

这是怎么回事,还是我的代码被破坏了?

这是为x86_64-linux-thread-multi构建的perl 5,版本16,版本3(v5.16.3)

1 个答案:

答案 0 :(得分:1)

正如评论中的Eric所述,浮点算法不是关联的;因此操作顺序会影响答案。

虽然“先添加较小的值”是一个很好的建议,但必须强调一点,即使用常规的“较小”的值也可能会有差异。这是一个示例:

  x  =  1.004028
  y  = 3.0039678
  z  =  4.000855

如果将这些视为IEEE-754单精度浮点数(即32位二进制格式),则我们得到:

  x + (y+z) = 8.008851
  (x+y) + z = 8.00885

无限精确的结果是8.0088508。所以都不是很好!而且该误差对于科学计算而言并非无关紧要,并且会累积。

这是一个内容丰富的领域,具有许多数值算法来确保精度。尽管您选择哪种方法完全取决于您的问题领域以及特定的需求和可用资源,但最著名的算法之一就是Kahan的求和算法,请参见:https://en.wikipedia.org/wiki/Kahan_summation_algorithm。您可以轻松地将其用于问题,以(希望)获得更好的结果。