Eigen sum()、colwise().sum().sum() 和 rowwise().sum().sum() 都给出了不同的答案

Eigen sum(), colwise().sum().sum() and rowwise().sum().sum() all give different answers

我有这个示例代码:

#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

这三个值不应该都一样吗?毕竟我没有使用优化。

你的加法基本上是随机游走,你犯的错误是不同的随机游走(因为你几乎每一步都有舍入误差)。 (注意 Eigen::MatrixXf::Random[-1, 1] 中的随机值填充矩阵。)

让我们假设您的浮点值平均为 10.0(仅根据您提供的单个数据点估算)。因此,您的 epsilon(任何加法可能会产生多少绝对舍入误差)约为 10.0 * 6e-8(float epsilon 为 2-23 或约 6e-8)或关于 6e-7.

如果你执行 N = 1000000 步长 +6e-7(或 -6e-7)的随机误差累积步骤,你很有可能在 sqrt(N) * stepSize = 1000 * 6e-7 = 6e-4 左右结束(参见 here),这与您的 0.01%.

不太巧合。

由于浮点精度,我会类似地估计在 -1 和 1 之间添加 100 万个随机双精度值的绝对误差 1000 * 10 * 1e-16 = 1e-12

这显然不是严格的数学处理。它只是表明错误肯定在正确的范围内。

减少此问题的常用方法是在添加浮点数之前按升序对浮点数进行排序,但这样做时您仍然可以任意不精确。 (示例:继续将数字 1.0f 添加到自身 - 总和将在 epsilon 变得大于 1.0f2^24 处停止增加。)