The only portable way is to use memcpy (yes, I know what you're thinking, and no it's not inefficient).
Note that this solution does not take into account byte-ordering. You'd need to cater for that too to be strictly portable.
#include <cstdlib>
#include <cstring>
#include <utility>
#include <iostream>
// only compile this function if Integer is the same size as a double
template<class Integer, std::enable_if_t<sizeof(Integer) == sizeof(double)>* = nullptr>
double or_bits(double input, Integer bits)
{
  Integer copy;
  // convert the double to a bit representation in the integer 
  std::memcpy(©, &input, sizeof(input));
  // perform the bitwise op
  copy |= bits;
  // convert the bits back to a double
  std::memcpy(&input, ©, sizeof(copy));
  // return the double
  return input;
}
int main()
{
  double d = 1.0;
  d = or_bits(d, 0x10ul);
  std::cout << d << std::endl;
}
assembly output on gcc5.3:
double or_bits<unsigned long, (void*)0>(double, unsigned long):
    movq    %xmm0, %rax
    orq     %rdi, %rax
    movq    %rax, -8(%rsp)
    movsd   -8(%rsp), %xmm0
    ret