Собственные суммы (), colwise (). Sum (). Sum () и rowwise (). Sum (). Sum () - все дают разные ответы) - PullRequest
3 голосов
/ 19 марта 2019

У меня есть этот пример кода:

#include <Eigen/Eigen>
#include <iostream>

int main() {
  Eigen::MatrixXf M = Eigen::MatrixXf::Random(1000, 1000);
  std::cout.precision(17);
  std::cout << M.colwise().sum().sum() << std::endl;
  std::cout << M.rowwise().sum().sum() << std::endl;
  std::cout << M.sum() << std::endl;
}

Я компилирую с помощью следующей команды: (g ++ версия 7.3, но я видел это и с другими компиляторами)

g++ -O0 -o test -Ieigen-3.3.7 test.cc

И вывод

13.219823837280273
13.220325469970703
13.217720031738281

Разве все эти 3 значения не должны быть одинаковыми? Я не использую оптимизацию в конце концов.

1 Ответ

7 голосов
/ 19 марта 2019

Ваши добавления - это, в основном, случайное блуждание, а ошибка, которую вы делаете, - это другое случайное блуждание (потому что у вас есть ошибка округления почти на каждом шаге). (Обратите внимание, что Eigen::MatrixXf::Random заполняет матрицу случайными значениями в [-1, 1].)

Предположим, что в среднем вы используете значение с плавающей запятой 10.0 (оценивается только по той единственной точке данных, которую вы указали). Ваш эпсилон (сколько абсолютной ошибки округления вы, вероятно, допустите при любом добавлении), таким образом, составляет около 10.0 * 6e-8 (число с плавающей запятой 2 -23 или около 6e-8 ) или около 6e-7.

Если вы выполните N = 1000000 случайные шаги накопления ошибок с размером шага +6e-7 (или -6e-7), у вас будет хороший шанс оказаться на отметке sqrt(N) * stepSize = 1000 * 6e-7 = 6e-4 (см. здесь ) что не слишком совпадает с вашим 0,01%.

Я бы также оценил абсолютную ошибку в 1000 * 10 * 1e-16 = 1e-12 для сложения 1 миллиона случайных двойных чисел от -1 до 1 из-за точности с плавающей запятой.

Это явно не строгая математическая обработка. Это просто показывает, что ошибка, безусловно, в правильном поле.

Распространенным способом решения этой проблемы является сортировка чисел с плавающей точкой в ​​порядке возрастания величины перед их добавлением, но при этом вы все равно можете быть произвольно неточными. (Пример: продолжайте добавлять число 1.0f к себе - сумма перестанет увеличиваться на 2^24, где эпсилон станет больше 1.0f.)

...