3

I have

std::vector<double>x={1,2,NAN,3, NAN, 4}

when I try x.erase(remove(x.begin(), x.end(), NAN), x.end(), I still see NAN as

for (auto i:x)
    std::cout<<i<<" ";

gives

1 2 -nan(ind) 3 -nan(ind) 4

how can I remove the NANs so the output is below?

1 2 3 4

EDIT: x is a vector of doubles, not ints

user5739619
  • 1,430
  • 3
  • 17
  • 32

2 Answers2

8

In the weird, wild world of IEEE 754 floating point, NaN != NaN. In other words, two not-a-number values do not compare as equal to each other. So, you cannot simply let std::remove do an equality test.

You need to supply your own predicate that calls a function to determine whether or not a floating-point value is NaN. If you're targeting C++11 or later, the standard library provides such a function: std::isnan. (If not, the situation is a bit more complicated.)

Putting it all together, then, the correct code would look like this:

#include <cmath>
#include <vector>
#include <algorithm>
#include <iostream>


int main()
{
    std::vector<double> x = {
                              1,
                              2,
                              std::numeric_limits<double>::quiet_NaN(),
                              3,
                              std::numeric_limits<double>::quiet_NaN(),
                              4
                            };

    std::cout << "Before removing: ";
    for (auto i : x)
    {
        std::cout << i << " ";
    }
    std::cout << "\n";

    x.erase(std::remove_if(std::begin(x),
                           std::end(x),
                           [](const auto& value) { return std::isnan(value); }),
            std::end(x));

    std::cout << "After removing: ";
    for (auto i : x)
    {
        std::cout << i << " ";
    }

    return 0;
}

In case it got lost in all the demo code, the relevant part is just:

x.erase(std::remove_if(std::begin(x),
                       std::end(x),
                       [](const auto& value) { return std::isnan(value); }),
        std::end(x));

where I am passing a lambda to std::remove_if as a unary predicate. It calls std::isnan, passing in the value to check, and it returns the result, which is a Boolean indicating whether or not the value is not-a-number. If the predicate returns true, std::remove_if does the removal; otherwise, it doesn't.

Note that the logic in floating-point world is typically that NaN values are infectious. In other words, any operation involving a NaN will return NaN as a result. So, it strikes me as a bit odd that you're trying to remove all NaN values. Be careful not to just shove them under the rug as if they weren't there.

Scheff's Cat
  • 16,517
  • 5
  • 25
  • 45
Cody Gray
  • 222,280
  • 47
  • 466
  • 543
2

This doesn't work, since NAN doesn't equal NAN (this is not specified by C++ standard, but in IEEE 754 standard, which your system probably conforms to). std::remove, uses equality as the condition to choose elements for removal.

Instead of std::remove, you need to use std::remove_if with a predicate using std::isnan.

Scheff's Cat
  • 16,517
  • 5
  • 25
  • 45
eerorika
  • 181,943
  • 10
  • 144
  • 256