Search code examples
c++vectornaneigencomparison-operators

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


See the code below:

    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;

Gives a output of:

0, 0, 1

However I do not want to lose the NaN value so my desired output would be as follows:

0, nan, 1

I know I can achieve this by looping over the initial vectors, recording the positions of any NaN values, and then updating the output with these positions.

However this is a bit messy and I want the code to be as efficient as possible (i.e. avoid any unnecessary for loops).

So my question is, is there an easy way to achieve this comparison without losing the NaN value?


Solution

  • It is possible but there are a few things to be aware of.

    First is that the type of the comparison result will not be the same. The result of component-wise comparison in Eigen will be an array of integers (0 or 1). If you want something that can be 0, 1 or NaN you will have to transform it into float. In Eigen you have to use an explicit cast operation.

    Note that, as pointed by the comments, the result will not tell you which side of the inequality had NaNs in the first place.

    Now, you can do this arithmetically. If we convert everything to vectors of float we can rely on the arithmetic properties of NaNs to propagate them :

    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;
    }
    

    This relies on the fact that x-x will yield 0 if x is a regular value and NaN if x is NaN, so vec1-vec1 will have 0 where its component values are regular numbers and NaN everywhere else.

    With the final addition the NaNs on the zero_or_nan vectors will contaminate the regular values on the rows containing NaN in the original vector.