Добавление массива с плавающей точкой приводит к странным суммам, добавляющим вперед или назад - PullRequest
0 голосов
/ 19 июня 2019

Я добавляю (суммирую) и массив чисел с плавающей точкой в ​​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).

Это то, что происходит, или мой код взломан?

Это Perl 5, версия 16, Subversion 3 (v5.16.3), созданная для x86_64-linux-thread-multi

1 Ответ

1 голос
/ 19 июня 2019

Как упоминал Эрик в комментариях, арифметика с плавающей запятой не ассоциативна; поэтому порядок выполнения операций повлияет на ответ.

Хотя «сначала добавьте меньшие значения» - это хороший совет, важно подчеркнуть, что вы можете иметь различия даже с обычными «маленькими» значениями. Вот один пример:

  x  =  1.004028
  y  = 3.0039678
  z  =  4.000855

Если принять за плавающие одинарной точности IEEE-754 (то есть 32-битный двоичный формат), то получим:

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

Бесконечно точный результат - 8.0088508. Так что ни один из них не очень хорош! И ошибка не незначительна для научных расчетов и накапливается.

Это богатое поле со множеством численных алгоритмов для обеспечения точности. Хотя выбор того, который вы выберете, полностью зависит от вашей проблемной области, а также от конкретных потребностей и ресурсов, которые у вас есть, одним из самых известных алгоритмов является алгоритм суммирования Кахана, см .: https://en.wikipedia.org/wiki/Kahan_summation_algorithm. Вы можете легко перенести его в свою задачу для ( надеюсь) лучшие результаты.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...