Walletfox.com

Careful with initial values passed to STL functions

There are many advantages of STL functions to hand-crafted for loops. They make your code generic and less error-prone to indexing and integer type issues. Yet, they are not without pitfalls.

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. Yet, 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 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
}

Explore I: Multiplication with std::accumulate

Adjust the previous code to perform multiplication.

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

// 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),  ,       );
  std::cout << res << '\n'; 
}

1. Which additional argument do you have to pass?

2. What initial value do you have to pass so that the code produces the expected result?

3. Do you see any difference between passing 1 as opposed to 1.0 as an initial value?

Explore II: Computing inner product

std::accumulate isn't the only function you need to be careful about. You are going to observe similar behavior with std::inner_product. Given two vectors a, b:

1. What is the result of the code below?

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

// requires C++17
int main(){
  auto const a = std::vector {1.5, 2.5};
  auto const b = std::vector {2.5, 3.5};

  auto res = std::inner_product(std::begin(a), std::end(a), std::begin(b), 0); 
}

2. What happens if you change 0 to 0.0?

3. Why did the original (incorrect) code produced the value 11?

Summary card: TRAP 2

  PDF | Code

Tagged: C++