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_element
1:
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 anoperator<
.
Compare
is a function object type ([function.objects]) that meets the requirements for a template parameter namedBinaryPredicate
([algorithms.requirements]). The return value of the function call operation applied to an object of typeCompare
, when contextually converted tobool
([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 usesoperator<
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 allx
), 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 defineequiv(a, b)
as!comp(a, b) && !comp(b, a)
, then the requirements are thatcomp
andequiv
both be transitive relations:(4.1)
comp(a, b) && comp(b, c)
impliescomp(a, c)
(4.2)
equiv(a, b) && equiv(b, c)
impliesequiv(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.