C++ Exploring The Darker Corners

 TRAP 2:
 Careful with initial values passed to STL functions

If you belong to the developers that prefer STL functions to hand-crafted for loops, you have probably already encountered the issue of initial values passed to the STL functions such as std::accumulate and std::inner_product. If you haven't encountered this issue yet, this section is for you.

Have a look at the code below. At first sight, you might not notice anything peculiar. Our intention was to add up all the values of the vector which is what the function appears to be doing. However, once you output the final value, you will get a surprise. The result is 2. Why is that? What is happening?

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

// WRONG!
// requires C++17 due to std::vector{...} rather than std::vector<int>{...}
int main(){
  auto const v = std::vector {1.7, 0.25, 1.3, 0.8};
  auto res = std::accumulate(std::begin(v),std::end(v),0);
  std::cout << res << '\n'; // res is 2
}

The reason for the surprise is the fact that you passed 0 as an initial value of the std::accumulate function. 0 is by default an int. Behind the scenes, truncation will happen at every application of the operator+. This effectively produces the same result as the following vector:

auto const v = std::vector {1, 0, 1, 0};

To correct the mistake we need to change 0 to 0.0 which is a double. Now the code produces the correct result 4.05.

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

// CORRECT!
// requires C++17
int main(){
  auto const v = std::vector {1.7, 0.25, 1.3, 0.8};
  auto res = std::accumulate(std::begin(v),std::end(v),0.0);
  std::cout << res << '\n'; // res is 2
}