Eigen,如何处理按元素除以零

Eigen, How to handle element-wise division by zero

如果某个分隔符可能为 0,如何处理 Eigen 中的逐元素除法?
我希望结果为 0。

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

using namespace Eigen;

int main()
{
  ArrayXd a(3), b(3), c;
  a << 1, 2, 0;
  b << 1.2, 3.4, 5.6;
  c = b / a;
}

我使用 binaryExpr 找到了这个解决方案,但不确定它是否有效。需要测试速度。我有数以万计的值要计算。

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

using namespace Eigen;

template<typename Scalar> 
struct SecureDivisionOp {
  SecureDivisionOp(Scalar v_ = 0) { v = v_; }
  Scalar v;
  Scalar operator()(const Scalar& a, const Scalar& b) const { 
    return b==0 ? v : a/b; 
  }
};

int main()
{
  ArrayXd a(3), b(3), c;
  a << 1, 2, 0;
  b << 1.2, 3.4, 5.6;

  c = b.binaryExpr(a, SecureDivisionOp<double>(0));
  std::cout << c << std::endl;
}

您可以使用 binaryExpr 处理被零除:

ArrayXd a(3), b(3);
a << 1, 2, 0;
b << 1.2, 3.4, 5.6;

Eigen::ArrayXd c = a.binaryExpr(b, [](auto x, auto y) { return y==0 ? 0 : x/y; });

如果你的编译器在 auto-vectorization 方面相当不错,你可以使用 Eigen 的 .select( ) 机制:

c = (a!=0).select(b/a, 0.0);

Clang 6 或更新版本(带有 -O3 -DNDEBUG -march=skylake)将把它的 main-loop 编译成等同于:

        vmovupd ymm1, ymmword ptr [rax]
        vcmpneqpd       ymm2, ymm1, ymm0
        vmaskmovpd      ymm3, ymm2, ymmword ptr [rdx]
        vdivpd  ymm1, ymm3, ymm1
        vandpd  ymm1, ymm2, ymm1
        vmovupd ymmword ptr [rax], ymm1

唯一轻微的性能改进是避免 vmaskmovpd——但从编译器的角度来看这是必要的,因为严格来说,并不是每个 i 都读取 b[i]其中 a[i]==0.