C++ Std::Accumulate Doesn't Give the Expected Sum

std::accumulate not acting as expected

You need to pass a double to accumulate:

result = accumulate(vec.begin(), vec.end(), 0.0, sum);
^^^

otherwise the accumulation is performed using int, and then converting the result to a double.

Why does std::accumulate generate 705032704 as output instead of the sum of the elements in the vector?

The standard algorithm std::accumulate is declared the following way

template<class InputIterator, class T>
T accumulate(InputIterator first, InputIterator last, T init);

So the template parameter T is used as the type of the accumulator.

In this call

std::accumulate(nums.begin(), nums.end(), 0)

the deduced type of the parameter T is int because the integer literal 0 has the type int. As a result there is an overflow of the sum stored in the accumulator of the type int.

You have to use at least the long long integer literal 0ll as

std::accumulate(nums.begin(), nums.end(), 0ll)

or maybe it is reasonable to use a double floating literal

std::accumulate(nums.begin(), nums.end(), 0.0 )

Here is a demonstrative program

#include <iostream>
#include <vector>
#include <iterator>
#include <numeric>

using ll = long long int;

int main()
{
std::vector<ll> nums = {1000000000, 1000000000,1000000000,1000000000,1000000000};
std::cout << std::accumulate( std::begin( nums ), std::end( nums ), 0ll ) << '\n';
std::cout << std::accumulate( std::begin( nums ), std::end( nums ), 0.0 ) << '\n';
}

Its output is

5000000000
5e+09

Using accumulate to calculate alternate sum

This is my quick solution :

int sign = -1; // start with minus first (e.g. 1-2)
std::vector<int> values{ 1, 2, 3, 4, 5 };

auto value = std::accumulate(values.begin(), values.end(), 0, [&sign](int total, int value)
{
sign = -sign;
return total + (sign)*value;
});

Understanding std::accumulate

The way things are, it is annoying for code that knows for sure a range isn't empty and that wants to start accumulating from the first element of the range on. Depending on the operation that is used to accumulate with, it's not always obvious what the 'zero' value to use is.

If on the other hand you only provide a version that requires non-empty ranges, it's annoying for callers that don't know for sure that their ranges aren't empty. An additional burden is put on them.

One perspective is that the best of both worlds is of course to provide both functionality. As an example, Haskell provides both foldl1 and foldr1 (which require non-empty lists) alongside foldl and foldr (which mirror std::transform).

Another perspective is that since the one can be implemented in terms of the other with a trivial transformation (as you've demonstrated: std::transform(std::next(b), e, *b, f) -- std::next is C++11 but the point still stands), it is preferable to make the interface as minimal as it can be with no real loss of expressive power.

std::reduce seems to convert results to integers

The version of std::reduce() that you are calling:

template<class ExecutionPolicy, class ForwardIt, class T, class BinaryOp>
T reduce(ExecutionPolicy&& policy,
ForwardIt first, ForwardIt last, T init, BinaryOp binary_op);

You can clearly see that the return value uses the same data type as the init parameter, which in your case is being deduced as an int, which is why the result is an int.

To make the return value be a double instead, simply change the literal 0 to 0.0 in the init parameter:

return reduce(execution::seq, cbegin(coeffs), cend(coeffs), 0.0, ...);

compute mean using std::accumulate fails

It seems that gcc uses accumulate<vector<double>::iterator,int> instead of accumulate<vector<double>::iterator,double>. If you use the specific template values it will work:

cout << "mean: " << accumulate<vector<double>::iterator,double>(v.begin(), v.end(), 0, mean) << endl;

EDIT: This happens because the type T in template< class InputIterator, class T >
T accumulate
is defined by your initial value 0, which is an integer. So use the line above or

cout << "mean: " << accumulate(v.begin(), v.end(), 0.0, mean) << endl;

References

  • http://en.cppreference.com/w/cpp/algorithm/accumulate
  • http://www.cplusplus.com/reference/std/numeric/accumulate/

How to sum up elements of a C++ vector?

Actually there are quite a few methods.

int sum_of_elems = 0;

C++03

  1. Classic for loop:

     for(std::vector<int>::iterator it = vector.begin(); it != vector.end(); ++it)
    sum_of_elems += *it;
  2. Using a standard algorithm:

     #include <numeric>

    sum_of_elems = std::accumulate(vector.begin(), vector.end(), 0);

    Important Note: The last argument's type is used not just for the initial value, but for the type of the result as well. If you put an int there, it will accumulate ints even if the vector has float. If you are summing floating-point numbers, change 0 to 0.0 or 0.0f (thanks to nneonneo). See also the C++11 solution below.

C++11 and higher


  1. b. Automatically keeping track of the vector type even in case of future changes:

     #include <numeric>

    sum_of_elems = std::accumulate(vector.begin(), vector.end(),
    decltype(vector)::value_type(0));
  2. Using std::for_each:

     std::for_each(vector.begin(), vector.end(), [&] (int n) {
    sum_of_elems += n;
    });
  3. Using a range-based for loop (thanks to Roger Pate):

     for (auto& n : vector)
    sum_of_elems += n;

C++17 and above


  1. Using std::reduce which also takes care of the result type, e.g if you have std::vector<int>, you get int as result. If you have std::vector<float>, you get float. Or if you have std::vector<std::string>, you get std::string (all strings concatenated). Interesting, isn't it?

    auto result = std::reduce(v.begin(), v.end());

    There are other overloads of this function which you can run even parallelly, in case if you have a large collection and you want to get the result quickly.



Related Topics



Leave a reply



Submit