2

I have the following code which returns me the sum of the last count elements in a vector of doubles foo:

return std::accumulate(foo.rbegin().base() - std::min(count, foo.size()), foo.rbegin().base(), 0);

But it is ignoring any decimal part. Why?

Wintermute
  • 42,983
  • 5
  • 77
  • 80
P45 Imminent
  • 8,319
  • 4
  • 35
  • 78

2 Answers2

6

It's surprisingly simple.

The type of the final parameter sets the type of the return of std::accumulate.

The simplest thing to do is use 0.0 in place of your final 0:

return std::accumulate(foo.rbegin().base() - std::min(count, foo.size()), foo.rbegin().base(), 0.0);

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
  • If you often use accumulate with 0 as last argument, you can also define your own wrapper that calls accumulate with a 0 of the right type. – Marc Glisse Feb 23 '15 at 11:47
  • I don't like this solution very much, because it (very silently) assumes the value type of `foo` to be `double`. If the value type of `foo` is changed in the future, you have almost no chance to fix this line of code accordingly. It may not be very critical with `float` vs. `double`, though. – leemes Feb 23 '15 at 12:00
  • Determining "the right type" was non-trivial when I tried it: http://stackoverflow.com/questions/26139083/sfinae-check-for-operator – MSalters Feb 23 '15 at 12:05
  • Indeed @leemes. I upvoted your answer. My answer is a cheat in the sense that I work with the OP on mathematical / scientific code and so I know his codebase. Using 0.0 is quite idiomatic in such code. – Bathsheba Feb 23 '15 at 12:05
3

Because you use 0 as the last parameter (the "initial value"), which is of type int; and this deduces T = int. (*)

I recommend to use foo.value_type() as the last parameter. This evaluates to zero for primitive types such as int, float, double, etc.

You can wrap this in a function template accumulate_zero to save some redundant work if needed often. The following function template implements this by setting T to the value type of the passed iterator:

template<class InputIt,
         class BinaryOperation,
         class T = typename std::iterator_traits<InputIt>::value_type>
//                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
T accumulate_zero(InputIt first, InputIt last, BinaryOperation op)
{
    return std::accumulate(first, last, T(), op);
    //                                  ^^^
}

(*) That's simply how type deduction works. std::accumulate is a function template with, among others, the type T being a template parameter. If not given explicitly and if possible, it is deduced from the passed argument's type.

leemes
  • 44,967
  • 21
  • 135
  • 183