7

This question has probably being asked, but I searched and could not find the answer.

I'm implementing a toy virtual machine, where the OpCodes take the form:

std::tuple<int8_t, int64_t, int64_t> // instruction op1, op2

I'm trying to pack a double into one of the operands and read it back again when processing it. This doesn't work reliably.

double d = ...
auto a = static_cast<int64_t>(d);
auto b = static_cast<double>(a)
// sometimes, b != d

Is there a way to pack the bit representation of the double into an int64_t, and then read that bit pattern back get the same exact double as before?

Dess
  • 2,064
  • 19
  • 35
  • The questions that have been asked are probably http://stackoverflow.com/q/2544394/11683, discussing what you currently have, and http://stackoverflow.com/a/103868/11683, discussing what you probably want. – GSerg Mar 18 '17 at 18:31
  • Possible duplicate of [Is it safe to reinterpret\_cast an integer to float?](http://stackoverflow.com/questions/13982340/is-it-safe-to-reinterpret-cast-an-integer-to-float) – GSerg Mar 18 '17 at 18:35

2 Answers2

5

static_cast performs a value conversion - the fractionary part is always lost. memcpy is what you are after.

double d = ... 
int64_t a;
memcpy(&a, &d, sizeof(a));
double d2;
memcpy(&d2, &a, sizeof(d2));

Still, I would probably instead make the operands a union with a double and an int64_t (plus possibly other types that are interesting for your VM).

Matteo Italia
  • 123,740
  • 17
  • 206
  • 299
1

One way to make it work is to reinterpret the block of memory as int64_t/double, i.e. to do pointer casts:

double d = ...
auto *a = (int64_t*)&d;
auto *d2 = (double*)a;
auto b = *d2;
assert(d == b);

Note that we both assume here that double and int64_t are of the same size (64 bit). I don't remember now if it is a part of the standard.

freakish
  • 54,167
  • 9
  • 132
  • 169
  • Btw, you can do that even without pointers: `int64_t a = (int64_t &)d;`. – HolyBlackCat Mar 18 '17 at 19:50
  • 8
    This is Undefined Behaviour. std::memcpy is the only defined way. – Richard Critten Mar 18 '17 at 20:55
  • 2
    @RichardCritten Why is it UB? I mean as long as sizes are correct then what's undefined about it? – freakish Mar 18 '17 at 23:19
  • 2
    @freakish because the standard says so, you would need to check to see if a compiler supported an extension. _"... and it's undefined behavior to read from the member of the union that wasn't most recently written. Many compilers implement, as a non-standard language extension, the ability to read inactive members of a union."_ http://en.cppreference.com/w/cpp/language/union – Richard Critten Mar 19 '17 at 00:05
  • 1
    @RichardCritten Why are you talking about unions? How is that related to pointer casts with the same size? – freakish Mar 19 '17 at 09:19
  • 1
    @freakish This is well defined to cast to another pointer type and back. What is Implentation Defined is when you try to read the value of different pointer type. See this: http://stackoverflow.com/a/7832859/5405086. It's not UB! – xinaiz Mar 19 '17 at 09:36