Setting bits to double and compiling with g ++ optimization flags

I am trying to set the bit to double (IEEE Standard 754). Saying that I want to "build" 3, I would set the 51st and 62nd bits of the double floating-point representation, so that I get 1.1 * 2 in binary, which in decimal is 3. I wrote this simple basic

int main() { double t; uint64_t *i = reinterpret_cast<uint64_t*>(&t); uint64_t one = 1; *i = ((one << 51) | (one << 62)); std::cout << sizeof(uint64_t) << " " << sizeof(uint64_t*) << " " << sizeof(double) << " " << sizeof(double*) << std::endl; std::cout << t << std::endl; return 0; } 

The result of this will be

 8 8 8 8 3 

when compiling with g ++ 4.3 and without optimization. However, I get weird behavior if I add optimization flags -O2 or -O3. That is, if I just leave the main one as it is, I get the same result. But if I delete the line that outputs 4 sizeof, I get the output

 0 

The fixed version without output sizeof returns 3, correctly.

So, I am wondering if this is an optimizer error, or if I am doing something wrong here.

+4
source share
3 answers

Yes, you violate the rules of language aliases. Writing to an object of one type using a pointer to another type is not allowed (with some exceptions for char* ).

Since you never write double to anyone in code, the compiler is allowed to assume that t never assigned a value. (And to deduce this in itself :-)

GCC has an extension that allows you to write the value of one type and read it as another type if you put them in a union. This compiler is specific, though (but semi-portable, like others, should follow suit).

+3
source

Technically, you have undefined behavior, although it is clear that the intention of the standard is that this work in obvious cases, and it is perverted by the compiler to break it if it can see reinterpret_cast . If you know the endpoint of your platform, you can cause a problem by using uint8_t (character type) to manipulate bits or memcpy in uint64_t , then memcpy returns back to double .

g ++ will do this work if you use union . Provided that all access passes through the type of association. The standard explicitly forbids this, however (although it was the preferred solution on pre-standard days), and I used compilers where this did not work.

With g ++, there is also the -fnostrict-aliasing option, which will make it work.

+2
source

Try:

 int main() { volatile double t; // ^^^^^^^^ Tells the compiler this may be modified unexpectedly. volatile uint64_t& i = reinterpret_cast<volatile uint64_t&>(t); uint64_t one = 1; i = ((one << 51) | (one << 62)); std::cout << t << std::endl; } 
+1
source

All Articles