0

I am trying to perform an addition on a double atomically in a loop using a compare and exchange scheme with this function:

namespace my { template<typename value_type> value_type atomic_add(std::atomic<value_type>& operand, value_type value_to_add) { value_type old = operand.load(std::memory_order_consume); value_type desired = old + value_to_add; while (!operand.compare_exchange_weak(old, desired, std::memory_order_release, std::memory_order_consume)) desired = old + value_to_add; return desired; } } 

This is used in my code like so:

[ size of containers = 62, scalar = 318.0, values in containers between 0.0 and 55.0, all values are of type double ] for(size_t i = 0; i < container.size(); i++) { my::atomic_add<double>(Q, container2[i] - std::pow(container3[i], 2) / scalar); } 

The output is 0.57784502195324539.

However, replacing all my_atomic with the += operator, and replace all std::atomic<double> with double gives 0.52something, which is closer to what I was expecting.

Any idea why this is happening?

Thank you.

4
  • Welcome back to Stack Overflow! :) I think we will need a minimal reproducible example to figure this one out. Commented Nov 30, 2016 at 6:57
  • Will provide, thank you! Commented Nov 30, 2016 at 7:00
  • Can't you use std::atomic_fetch_add ? Commented Nov 30, 2016 at 7:09
  • @Arunmu Unfortunately not; std::atomic_fetch_add only works for integral types. In this case I need floats. Commented Nov 30, 2016 at 21:29

1 Answer 1

0

I seem to have fixed this issue so I am going to post an answer as a cautionary tale for other developers.

Here is a minimal working example:

#include <iostream> #include <atomic> #include <vector> namespace my { template<typename value_type> value_type atomic_add(std::atomic<value_type>& operand, value_type value_to_add) { value_type old = operand.load(std::memory_order_consume); value_type desired = old + value_to_add; while (!operand.compare_exchange_weak(old, desired, std::memory_order_release, std::memory_order_consume)) desired = old + value_to_add; return desired; } } int main() { std::vector<double> vector; for(int i = 0; i < 100; i++) { double a; a = 2.0; vector.push_back(a); } std::atomic<double> d; for(int i = 0; i < vector.size(); i++) my::atomic_add(d, vector[i]); std::cout << d << std::endl; } 

The output of which is 202. It appears that d is initialized to 2.0. The moral of this story is to always make sure that your std::atomics are initialized to a sane value.

Furthermore, other answers have pointed out that it is generally a bad idea to store std::atomicss in a vector as they are not CopyAssignable and CopyConstructible.

Sign up to request clarification or add additional context in comments.

3 Comments

I would recommend you edit your question to contain the example. The real reason for your problem, then, is undefined behavior: as you noticed, std::atomic's default ctor does not initialize the data. d happens to be 2.0 because the value happens to already be in that stack location from the previous loop (double a held the same place). If you change your code to a = 4.0, you'll likely get 404 as a result. But that is just what your compiler happens to do; really, you're just seeing UB.
See also stackoverflow.com/questions/36320008/… (your post turns out to be a duplicate).
@mindriot I will amend accordingly; It does turn out to be a duplicate, however my first suspicion was that this was a fringe floating-point error. Looking back this was a naive assumption.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.