Sign in to follow this  

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

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

If you intended to correct an error in the post then please contact us.

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 this post


Link to post
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 this post


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

Share this post


Link to post
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<unsigned int>(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 this post


Link to post
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

Share this post


Link to post
Share on other sites

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

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this