# Conversion of double to unsigned int in g++ with -O3 option

This topic is 4085 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Hello people, i have a question about the following piece of code:
#include <iostream>

unsigned int ToUInt32(double x)
{
x += 4503599627370496.0; // pow(2.0, 52.0)
return *((unsigned long long*)(&x)) & 0x00000000FFFFFFFF;
};

int main()
{
double x = 5.51;
std::cout << ToUInt32(x) << std::endl;

return 0;
};


The ToUInt32 function adds the magic number to x. The addition causes x's mantissa to shift to the right (at least this happens for all x's between 0 and 2^32 - 1), so that the rightmost bit of x is the bit with value 2^0. This way we can convert to an unsigned int by just taking the rightmost 32 bits of x after the addition. We can also get our int scaled by any power of two (for free!! :P) just by dividing the value we add to x by 2 raised to the power we want to scale the output int by (so that the shift of the mantissa will be fewer bits to the right). For example if i want to get unsigned ints in the range of 0-255 for inputs in the range 0.0-1.0, i can add 4503599627370496.0 / 256.0 to x in stead of just 4503599627370496.0. This works fine in visual studio 2005 but in g++ 4.1 (i m very noob in linux) i have the following problem: When i compile without optimization or with just -O it seems to work ok. But if i compile with -O2 or -O3 i get somewhat random results like these: 3216858152 or 3214622984, all for the same input of 5.51 which is supposed to give 6 as a result (the method is suppsed to round, not truncate). I tried to use reinterpret_cast<unsigned long long*> too but without luck. Maybe i m using some undefined stuff?

##### Share on other sites
Quote:
 Original post by D_Tr[...]Maybe i m using some undefined stuff?
Yes, using a pointer to type X as a pointer to type Y is not specified by the standard as having any particular behavior.

The proper way to cast in this case is static_cast<unsigned int>(x + 0.5) if you want X.5 or more to round up.

##### Share on other sites
Maybe try:
    union {        unsigned long long ull;        double d;    } tmp;    tmp.d = x;    return tmp.ull & 0x00000000FFFFFFFFULL;

##### Share on other sites
Quote:
 Original post by bubu LVMaybe try:*** Source Snippet Removed ***
While that may work around the optimizer, it's still undefined behavior.

##### Share on other sites
First of all, thx for the replies!

@bubu LV:

A slightly modified version of your code worked :) it just needed a slight modification so that
tmp.d = x;
becomes
tmp.d = x + 4503599627370496.0; so that the mantissa shift occurs to bring the bits into the right places. This union thingie is what i expected the pointer cast to do (read the bits of the double as an int), but yours is more standard / correct as it seems.

@Extrarius:
Quote:
 Yes, using a pointer to type X as a pointer to type Y is not specified by the standard as having any particular behavior.

Thx for the info :]

Quote:
 The proper way to cast in this case is static_cast(x + 0.5) if you want X.5 or more to round up.

Yep i have that in mind, but i asked because:
1) With the magic number i can get back the int scaled and rounded without doing an extra multiplication and adding 0.5 so it might be faster than the standard way, sacrificing portability.
2) Just out of curiosity about the different behavior with different optimization flags. But since the pointer cast is not defined, i guess the compiler can change its behavior however and whenever it wants, as long as standard code remains ok...

##### Share on other sites
Quote:
 1) With the magic number i can get back the int scaled and rounded without doing an extra multiplication and adding 0.5 so it might be faster than the standard way, sacrificing portability.

It won't be faster. Compiler writers are super-human beings that know much more about optimisation than average-joe programmers such as you or I. In the unlikely event that your method does prove to be faster, it should be reported as a bug in the compiler.

Learn to trust the compiler and only rely on profiler data to guide low-level optimisations (and most high-level ones, too). Programmers are notoriously bad at guessing where the bottlenecks are.

Quote:
 2) Just out of curiosity about the different behavior with different optimization flags. But since the pointer cast is not defined, i guess the compiler can change its behavior however and whenever it wants, as long as standard code remains ok...

That's right. The compiler can do whatever it likes as long as the observed behaviour of your program matches that specified by the 'virtual machine' laid out in the standard. If your program contains code that exhibits "undefined behaviour", the program is allowed to do absolutely anything as far as the standard is concerned.

Sometimes a particular compiler vendor will offer extra guarantees about certain constructs, but it's not safe to rely on those as they're not portable and more prone to changing in future versions.

Edd

1. 1
2. 2
3. 3
Rutin
19
4. 4
5. 5

• 10
• 14
• 30
• 13
• 11
• ### Forum Statistics

• Total Topics
631782
• Total Posts
3002338
×