Home > Software engineering >  How to prevent std::min and max to return NAN if the first element of the array is NAN?
How to prevent std::min and max to return NAN if the first element of the array is NAN?

Time:05-20

Is there a way to make min/max (std::min_element) ignore all NANs? I mean, it seems to ignore NANs in the middle but not if the first element is NAN.

Sample:

template <typename T> inline void GetMinMax(const T* data, const int len, T& min, T& max)
{
    min = *std::min_element(data, data   len);
    max = *std::max_element(data, data   len);
}

float min, max;
std::array<float, 4> ar1 = { -12, NAN, NAN, 13 };
GetMinMax<float>(&ar1[0], ar1.size(), min, max); //min: -12. max: 13

std::array<float, 4> ar2 = { -12, 3, 13, NAN };
GetMinMax<float>(&ar2[0], ar2.size(), min, max);//min: -12. max: 13

std::array<float, 4> ar3 = { NAN, -12, 3, 13 };
GetMinMax<float>(&ar3[0], ar3.size(), min, max);//min: -nan(ind). max: -nan(ind)  !!!!

CodePudding user response:

The safest path is to remove all the NaNs values from the range before applying any min-max standard algorithm.

Consider a possible implementation of std::min_element1:

template<class ForwardIt>
ForwardIt min_element(ForwardIt first, ForwardIt last)
{
    if (first == last) return last;
 
    ForwardIt smallest = first;       // <-- If the first is a NaN...
      first;
    for (; first != last;   first) {
        if (*first < *smallest) {     // <-- This condition will always be FALSE
            smallest = first;
        }
    }
    return smallest;                  // <-- An iterator to a NaN is returned
}

More formally, the C Standard2 specifies:

27.8.1 General [alg.sorting.general]

The operations in [alg.sorting] defined directly in namespace std have two versions: one that takes a function object of type Compare and one that uses an operator<.

Compare is a function object type ([function.objects]) that meets the requirements for a template parameter named BinaryPredicate ([algorithms.requirements]). The return value of the function call operation applied to an object of type Compare, when contextually converted to bool ([conv]), yields true if the first argument of the call is less than the second, and false otherwise. Compare comp is used throughout for algorithms assuming an ordering relation.

For all algorithms that take Compare, there is a version that uses operator< instead. That is, comp(*i, *j) != false defaults to *i < *j != false. For algorithms other than those described in [alg.binary.search], comp shall induce a strict weak ordering on the values.

The term strict refers to the requirement of an irreflexive relation (!comp(x, x) for all x), and the term weak to requirements that are not as strong as those for a total ordering, but stronger than those for a partial ordering. If we define equiv(a, b) as !comp(a, b) && !comp(b, a), then the requirements are that comp and equiv both be transitive relations:

(4.1) comp(a, b) && comp(b, c) implies comp(a, c)

(4.2) equiv(a, b) && equiv(b, c) implies equiv(a, c)

The problem, given any float value x, is that the following hold:

x < NaN == false and NaN < x == false, but x != NaN

Only considering the subset of the float values which are not NaNs we can fulfill the requirement.


1) https://en.cppreference.com/w/cpp/algorithm/min_element

2) I'm quoting the draft at https://eel.is/c draft/alg.sorting.general , emphasis mine.

CodePudding user response:

NAN compares with other numbers is screwy, see What is the result of comparing a number with NaN?.

So what you need to do is to provide a custom compare function so that NAN isn't the element std::min_element returns. Probably just make "NAN < x" return false and "x < NAN" return true. Similar for ">" for std::max_element. Or try overloading <=>.

Note: the solution might depend on your implementation of the STL, depending on which comparison operator the implementation uses.

  • Related