Jump to content
  • Advertisement
Sign in to follow this  
Ectara

C shifts

This topic is 2999 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

Does a compiler's optimization typically combine bit shifts? For example, would

((foo << 8) >> 2)

be optimized to

(foo << 6)?

Share this post


Link to post
Share on other sites
Advertisement
Given the small fact that those two expressions have different semantics, then it shouldn't do that "optimization".

Share this post


Link to post
Share on other sites
Generally, a compiler is only allowed to do legal optimizations, this one is not legal.

The results of ((foo << 8) >> 2 and (foo<<6) are not the same. The former will have the topmost two bits cleared, whereas the latter will have them filled with whatever was in the 6th-8th highest bits in foo before.

Share this post


Link to post
Share on other sites
In your case - no, because these two expressions will yield different results.

But, for example, if you compare (x << 2) << 4 to (x << 6) then compiler will generate exact same code in optimized builds.

Share this post


Link to post
Share on other sites
Good, I thought so. I know that some platforms do signed shifts left and unsigned right, or whatever they decide to do with that compiler.

Since I'm here, I may as well be more specific. I was thinking of shifting 0xFFFFFFFF left a certain amount of bits left, then right the same amount to clear the top bits before shifting right, to generate a mask. Are there any better methods of generating a mask given an amount of bits to clear from the top?

Share this post


Link to post
Share on other sites
For example using the & (bitwise and) operator, and a template that converts a number into a bitmask (making it a compile-time constant).

Share this post


Link to post
Share on other sites
To elaborate, you could make a recursive template for getting the power of two for a given number (search Google for "template metaprogramming" to learn how this works). Subtracting 1 from that will give you a bitmask of all bits below that one set, so you'll want to actually calculate the power of N+1 and subtract 1 afterwards.
This will give you a mask that can be used to clear the topmost N bits, or to do any other boolean trick :-)

EDIT: My bad... that mask would clear the topmost 8*sizeof(int) - N bits. But, you know what I mean.

Share this post


Link to post
Share on other sites
Doing 2 shifts should work just fine. I don't see the point of a template, if the inputs are known at compile-time, the compiler can figure it out.

One thing to watch out for, don't try shifting by 32 or more bits. You may not get 0 as expected. Some of my code depended on that at one time, and it took a long time to track down.

Share this post


Link to post
Share on other sites
The code would then look something like this:

template<int x> struct pow2 { enum { value = 2*pow2<x-1>::value }; };
template<> struct pow2<0> { enum { value = 1 }; };
...
const int mask_to_clear_topmost_3_bits = pow2<sizeof(int) - 3 + 1>::value - 1;
...
someinteger &= mask_to_clear_topmost_3_bits;


Mind you, I've not tried whether this compiles at all, but the general idea goes like that.


EDIT:
Quote:
Doing 2 shifts should work just fine. I don't see the point of a template
The point is that a compiler *might* figure what you want, but it might not just as well. Using operator& on the other hand is guaranteed to be optimal. Two shifts on the same register do not pipeline, so they are much slower than a single "and" with a constant. Plus, shifts are undefined behaviour, whereas "and" is not.

Share this post


Link to post
Share on other sites
For my own amusement, I tried it out in VS2010. Horray for optimizers!

unsigned int val;
std::cin >> val;
013D1004 mov ecx,dword ptr [__imp_std::cin (13D2048h)]
013D100A lea eax,[val]
013D100D push eax
013D100E call dword ptr [__imp_std::basic_istream<char,std::char_traits<char> >::operator>> (13D2044h)]
int otherVal = (val << 16) >> 24;
std::cout << otherVal;
013D1014 mov ecx,dword ptr [val]
013D1017 shr ecx,8
013D101A and ecx,0FFh
013D1020 push ecx
013D1021 mov ecx,dword ptr [__imp_std::cout (13D2050h)]
013D1027 call dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (13D204Ch)]

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!