27
votes

My question is quite simple. Why isn't std::atomic<double> implemented completely? I know it has to do with atomic RMW (read-modify-write) access. But I really don't see, why this shouldn't be possible on a double.

It's specified that any trivially copyable type can be used. And of course double is among them. So C++11 requires the basic operations (load, store, CAS, exchange, etc.) that you can use with any class type.

However, on integers an extra set of operations is possible (fetch_add, ++, +=, etc).

A double differs very little from these types. It's native, trivially copyable, etc. Why didn't the standard include the double with these types?


Update: C++20 does specialize std::atomic<T> for floating-point types, with fetch_add and sub. C++20 std::atomic<float>- std::atomic<double>.specializations But not atomic absolute-value (AND) or negate (XOR).

Editor's note: Without C++20 you can roll your own out of CAS; see Atomic double floating point or SSE/AVX vector load/store on x86_64 for portable examples; atomic<double> and float are lock-free on most C++ implementations.

2
I'd guess the reason is that most CPUs don't support atomic double operations. So how would you implement it?Angew is no longer proud of SO
@Damon: The standard doesn't care about registers. "Trivially copyable" is just about copying from one memory location to another using memcpy (simply put). The architecture would have to be very strange if it didn't support that.Arne Vogel
But anyway, SSE2 is baseline for 64-bit, and a lot of 32-bit software is built with SSE2 enabled. In that case, there's absolutely no weirdness. Either way, std::atomic<double> is lock-free on gcc/clang/msvc. stackoverflow.com/questions/45055402/…Peter Cordes
@curiousguy: "trivially copyable", not "copiable" was the correct spelling. I only mention this in case you were going to edit anything else to make the same change. The rest of your edit looks reasonable. "interlocked" was asking about the underlying implementation being possible using x86 Windows terminology which is sort of ok, but sure, HW support for atomic RMW is the same thing.Peter Cordes
@curiousguy: see docs.microsoft.com/en-us/windows/win32/api/winnt/… and friends, like InterlockedXor64Peter Cordes

2 Answers

20
votes

std::atomic<double> is supported in the sense that you can create one in your program and it will work under the rules of C++11. You can perform loads and stores with it and do compare-exchange and the like.

The standard specifies that arithmetic operations (+, *, +=, &, etc.) are only provided for atomics of "integral types", so an std::atomic<double> won't have any of those operations defined.

My understanding is that, because there is little support for fetch-add or any other atomic arithmetic operations for floating point types in hardware in use today, the C++ standard doesn't provide the operators for them because they would have to be implemented inefficiently.

(edit). As an aside, std::atomic<double> in VS2015RC is lock-free.

9
votes

The standard library mandates std::atomic<T> where T is any TriviallyCopyable type. Since double is TriviallyCopyable, std::atomic<double> should compile and work perfectly well.

If it does not, you have a faulty library.

Edit: since comment clarifying the question:

The c++ standard specifies specific specialisations for fundamental integral types. (i.e. types that contain integers that are required to be present in the language). These specialisations have further requirements to the general case of atomic, in that they must support:

  • fetch_add
  • fetch_sub
  • fetch_and
  • fetch_or
  • fetch_xor
  • operator++
  • operator--
  • comparison and assignment operators

OR, XOR, AND are of course not relevant for floating types and indeed even comparisons start to become tricky (because of the need to handle the epsilon). So it seems unreasonable to mandate that library maintainers make available specific specialisations when there is no case to support the demand.

There is of course nothing to prevent a library maintainer from providing this specialisation in the unlikely event that a given architecture supports the atomic exclusive-or of two doubles (it never will!).