0

I am a C++ noob.

What I am trying to do is sum the values of a vector of doubles (let's call it x) and ignore any values that are NaN. I tried to look this up, but I couldn't find anything specifically referencing what would happen if a vector contains any NaN values.

E.g.:

// let's say x = [1.0, 2.0, 3.0, nan, 4.0]
y = sum(x) // y should be equal to 10.0

Would the accumulate function work here? Or would it return NaN if x contains a NaN? Would a for loop work here with a condition to check for if the value is NaN (if yes, how do I check if NaN? In Python, the language I know best, this kind of check is not always straightforward).

Jinx
  • 511
  • 1
  • 3
  • 10
  • Does this answer your question? [Checking if a double (or float) is NaN in C++](https://stackoverflow.com/questions/570669/checking-if-a-double-or-float-is-nan-in-c) – kotatsuyaki Nov 29 '22 at 15:10
  • Use the isnan macro https://stackoverflow.com/questions/9240138/isnan-equivalent-in-c – Sven Nilsson Nov 29 '22 at 15:10
  • 1
    Once NaN gets into the sum it will be sticky. You need to write your own `sum` and use [`std::isnan`](https://en.cppreference.com/w/cpp/numeric/math/isnan) to skip NaN values. – Richard Critten Nov 29 '22 at 15:10
  • 1
    Any valid number + NaN == NaN; Filter-out all your NaNs before performing accumulate, or make summation by hand in the loop while checking for `std::isnan`. You can also use `std::accumulate` with your custom binary operator, but to make this operator implementation-robust you would need to do unnecessary checks for both lhs and rhs. – pptaszni Nov 29 '22 at 15:11

2 Answers2

3

std::isnan returns true if the passed floating point value is not a number. You have to add this check to all functions to avoid including NANs in your calculations. For example for sum:

constexpr auto sum(auto list) {
    typename decltype(list)::value_type result = 0;

    for (const auto& i : list) {
        if (!std::isnan(i)) { // < - crucial check here
            result += i;
        }
    }
    return result;
}

Demo:

int main() {
    auto list = std::array{ 1.0f, 2.0f, 3.0f, NAN };
    std::cout << sum(list); //prints out 6
}
Stack Danny
  • 7,754
  • 2
  • 26
  • 55
1

you could use std::accumulate with a custom summation operation.

const std::vector<double> myVector{1.0, 2.0, 3.0, std::nan("42"), 4.0};

auto nansum = [](const double a, const double b)
{
  return a + (std::isnan(b) ? 0 : b);
}

auto mySum = std::accumulate(myVector.begin(), myVector.end(), 0.0, nansum);
Willi
  • 11
  • 2
  • 2
    Typical mistake mentioned in the docs: your sum will be casted to int if you run this operation on a vector with some decimal values. Correct version: `std::accumulate(myVector.begin(), myVector.end(), 0.0, nansum);` – pptaszni Nov 29 '22 at 15:39