Eigen 在不丢失 NaN 值的情况下将向量与某些 NaN 值进行比较

Eigen compare vectors with some NaN values WITHOUT losing the NaN values

查看下面的代码:

    Eigen::VectorXf vec1(3);
    vec1(0) = 231;
    vec1(1) = 512;
    vec1(2) = 213;
    Eigen::VectorXf vec2(3);
    vec2(0) = 10;
    vec2(1) = std::numeric_limits<double>::quiet_NaN();
    vec2(2) = 2213;

    std::cout << (vec1.array() < vec2.array()) << std::endl;

输出:

0, 0, 1

但是我不想丢失 NaN 值,所以我想要的输出如下:

0, 南, 1

我知道我可以通过遍历初始向量,记录任何 NaN 值的位置,然后用这些位置更新输出来实现这一点。

但是这有点混乱,我希望代码尽可能高效(即避免任何不必要的 for 循环)。

所以我的问题是,有没有一种简单的方法可以在不丢失 NaN 值的情况下实现这种比较?

这是可能的,但有几点需要注意。

首先是比较结果的类型不会相同。 Eigen 中按分量比较的结果将是一个整数数组(0 或 1)。如果您想要可以是 0、1 或 NaN 的值,则必须将其转换为 float。在 Eigen 中,您必须使用显式转换操作。

请注意,正如评论所指出的,结果不会告诉您不等式的哪一侧首先有 NaNs。

现在,您可以算术计算了。如果我们将所有内容都转换为 float 的向量,我们可以依靠 NaN 的算术属性来传播它们:

VectorXf compare_with_nans(const VectorXf& vec1, const VectorXf& vec2) {

  const VectorXf zero_or_nan_1 = vec1 - vec1;
  const VectorXf zero_or_nan_2 = vec2 - vec2;

  const VectorXf compare = (vec1.array() < vec2.array()).matrix().cast<float>();

  return compare + zero_or_nan_1 + zero_or_nan2;
}

这取决于以下事实:如果 x 是常规值,x-x 将产生 0,如果 x 是 [=11=,则 NaN ],因此 vec1-vec1 将在其组件值为常规数字的位置为 0,在其他任何地方为 NaN

最后添加 zero_or_nan 向量上的 NaN 将污染原始向量中包含 NaN 的行上的常规值。