I create a simple array of 100 equal values of 0.1 floats and sum them up using the following straightforward code:
1 2 3 4 5 6 7 8 9 10 11 |
#include <array> #include <numeric> #include <iostream> int main() { std::array<float, 100> a; std::fill(a.begin(), a.end(), 0.1f); std::cout << std::accumulate(a.begin(), a.end(), 0) << std::endl; return 0; } |
The expected result is 10. Why this simple program prints the result 0 ?
If you haven’t met the function std::accumulate in detail before and not very experienced in C++ templates and type deduction, the answer will not come easy. First of all: This is not a bug in standard library 🙂 This is just a feature of C++ type deduction of template functions. To be short, let’s have a look at the possible implementation of std::accumulate
1 2 3 4 5 6 7 8 |
template<class InputIt, class T> T accumulate(InputIt first, InputIt last, T init) { for (; first != last; ++first) { init = std::move(init) + *first; // std::move since C++20 } return init; } |
and concentrate on the declaration. It is a template function, where the first template parameter defines the type of the iterator to the begin and the end point of the container to sum up. The second template parameter defines not only the type of the (mostly neglected) third parameter, which is the initial sum value. But this is the type of the return value!!! That means, the type deduced from the third parameter is also the type of the result. The variable init, which is initially the integer 0 in the example above, is being increased by 0.1 truncated (not rounded) to integer 100 times. Clearly, the result is 0.
The proper way of using std::accumulate and similar template functions is to look at their declaration and take care of the types of the input parameters. In case of std::accumulate the third parameter should have exactly the type of the return value:
1 |
std::cout << std::accumulate(a.begin(), a.end(), 0.f) << std::endl; |
Much better than the C-style constant definition is the following C++11-style constant initialization, which is nicely works with classes as well as build-in types:
1 |
std::cout << std::accumulate(a.begin(), a.end(), float{}) << std::endl; |