首页 > 解决方案 > Eigen 版本 3.2.0 和 3.3.4 之间的数值差异

问题描述

我最近推出了一个使用一些基本向量数学和SelfAdjointEigenSolverEigen 3.2.0 到 3.3.4 的代码。该代码是用 Matlab 制作的,它与 3.2.0 一致。滚动到 3.3.4 的动机是避免在 C++11(及更高版本)和 3.2.0 中出现的一些警告和错误。

该代码通过将数千个点读入Eigen::Matrix<double, Eigen::Dynamic, 3>. 当我使用 gdb 检查值时,输入值是相同的。一段显示数字差异的代码如下所示。 pilType定义为double

// this is the point cloud, center it about the origin
Eigen::Matrix<pilType, 1, 3> center = gridPnts.colwise().mean();
gridPnts.rowwise() -= center;
// Get the transformation matrix to align the point cloud with it's normal
// Build the co variance matrix
Eigen::Matrix<double, 3, 3> S = gridPnts.transpose() * gridPnts;
S /= static_cast<pilType>(gridPnts.rows() - 1);
Eigen::SelfAdjointEigenSolver<pilMat3> es(S);
Eigen::Matrix<pilType, 3, 2> trans;
trans = es.eigenvectors().block<3, 2>(0, 1);
// convert the point cloud to 2D for Convex Hull calculation
Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> output(gridPnts.rows(), 2);
output = gridPnts * trans;

我正在使用 GCC 5.3.0 编译新版本,标志 -std=c++11 -O3 -DEIGEN_NO_DEBUG -Wall -Wextra -Werror -march=native -mtune=native。我尝试过使用和不使用 March 和 mtune 选项。旧版本使用 GCC 4.3.4 和标志 -O3 -DEIGEN_NO_DEBUG -Wall -Wextra -Werror 编译。CPU 是运行 SuSE Enterprise SLES 11 的 SandyBridge E5-2670。差异在划分后开始S通过点数 - 1。它们非常小,大约为 1e-12,但是当涉及到特征解时,非常接近 0 的值会导致特征向量中的符号发生变化。这会导致转换矩阵针对一个轴进行切换。结果输出沿该轴翻转。生成的 2D 补丁被发送到一个例程以找到凸包。由于轴翻转,返回相同的点但顺序相反。这不是错误,但出乎意料。

事实上,在整个申请中,不同的数字显示了 1e-12 到 1e-15 范围内的差异。

以下是使用 GDB 运行时的一些值(最高值来自 3.3.4 版本,较低值来自 3.2.0)

S划分前:

    Eigen::Matrix<double,3,3,ColMajor> (data ptr: 0x7fffffffb0b0) = {[0,0] = 3532221.7020642869,
  [1,0] = 1.0913936421275139e-11, [2,0] = 3332.4628071428679, [0,1] = 1.0913936421275139e-11,
  [1,1] = 335265.83999999962, [2,1] = -1.3073986337985843e-12, [0,2] = 3332.4628071428679,
  [1,2] = -1.3073986337985843e-12, [2,2] = 4697.4509785714226}

Eigen::Matrix<double,3,3,ColMajor> (data ptr: 0x7fffffffb8f0) = {[0,0] = 3532221.7020642869,
  [1,0] = 1.0913936421275139e-11, [2,0] = 3332.4628071428679, [0,1] = 1.0913936421275139e-11,
  [1,1] = 335265.83999999962, [2,1] = -1.3073986337985843e-12, [0,2] = 3332.4628071428679,
  [1,2] = -1.3073986337985843e-12, [2,2] = 4697.4509785714226}

S分工后

Eigen::Matrix<double,3,3,ColMajor> (data ptr: 0x7fffffffb0b0) = {[0,0] = 21151.028156073575,
  [1,0] = 6.5352912702246338e-14, [2,0] = 19.954867108639927, [0,1] = 6.5352912702246338e-14,
  [1,1] = 2007.5798802395186, [2,1] = -7.8287343341232597e-15, [0,2] = 19.954867108639927,
  [1,2] = -7.8287343341232597e-15, [2,2] = 28.128448973481571}

Eigen::Matrix<double,3,3,ColMajor> (data ptr: 0x7fffffffb8f0) = {[0,0] = 21151.028156073575,
  [1,0] = 6.5352912702246351e-14, [2,0] = 19.954867108639927, [0,1] = 6.5352912702246351e-14,
  [1,1] = 2007.5798802395188, [2,1] = -7.8287343341232597e-15, [0,2] = 19.954867108639927,
  [1,2] = -7.8287343341232597e-15, [2,2] = 28.128448973481575}

在执行特征解之后,其值为trans

{[0,0] = 3.4096942364292876e-18, [1,0] = -1,
  [2,0] = 3.9893751393767394e-18, [0,1] = 0.99999955376919869, [1,1] = 3.4134614846087836e-18,
  [2,1] = 0.00094470175363752104}

{[0,0] = 0, [1,0] = 1,
  [2,0] = -3.2750362278258563e-15, [0,1] = 0.9999995537691988, [1,1] = 3.093932467653499e-18,
  [2,1] = 0.00094470175363752125}

诚然,数值差异非常小,并且在准确性的噪音范围内。在显示的数字中,变换矩阵的符号翻转,导致 2D 云翻转。有没有办法清理所做的事情以使这些差异消失?我见过一些Eigen具有小值阈值的函数,但我没有看到一个用于除法的函数。

标签: c++precisioneigen3

解决方案


您根本不应该依赖特征向量上的符号(无论求解器和库如何),而是根据您的需要调整它们。例如,如果您想确保有一个右手基,则从前两个的叉积中更新最后一个。


推荐阅读