What's new

Bit shifting: I must have missed something...

Toasty

Sony battery
Am stuck on my laptop with Ubuntu until I get my desktop motherboard replaced, and since Linux isn't too great in the gaming department I figured I'd do a little programming. I noticed one of my functions was producing some incorrect results though and finally tracked down the problem to something that has thoroughly mystified me. Here's a simple example:

main.cpp
Code:
#include <stdio.h>

#define NUM 1

int main(int argc, char** argv) {

	unsigned long long test = NUM;

	//Let's shift right by 64 bits - in two steps
	unsigned long long r1 = (test >> 63) >> 1;

	//Let's shift right by 64 bits - in one step
	unsigned long long r2 = (test >> 64); //A compiler warning here

	//Print out r1 and r2 on separate lines, formatted as hex
	//These *should* be identical, right?
	printf("%llX\n%llX\n", r1, r2);
	
	return 0;
	
}
As expected, g++ was a little upset with me about the line where I shifted test down by 64 bits (warning: right shift count >= width of type), but it was only a warning and compiled fine. The warning of course makes sense, as that command (both commands actually, though the compiler didn't figure the first one out) would shift away all the bits in test (leaving 0 as the result, no matter what test's value is). At least, I thought that's what would happen. I figured I would receive two results printed, both of which being 0, but instead, I get:
Code:
0
1
So I'm completely confused. I thought shifting an n-bit integer right (or left, for that matter) by n bits would always result in zero. Am I missing something?
 

Exophase

Emulator Developer
g++ was right to inform you, because your expectations were incorrect. The actual behavior is not defined by C/C++ and is left to implementation.

However, generally speaking, the instructions will only store enough bits to encode a shift amount equal to the number of bits in the element you're shifting. In your case only the lower 6 bits of the 64 are actually encoded in the instruction, resulting in what's a shift by 0.

The ARM architecture actually reserves 0 for special meaning that isn't "shift by zero" at all, since this isn't something you'd normally want to do. But if you try to outright shift by zero then chances are the compiler will optimize it away for you.

Things are more platform dependent when you shift by a variable amount, where there isn't such a direct limitation on the number of bits in the shift operand. I believe on x86 only the lower bits are taken, but on ARM an entire 8 bits are taking, resulting in the ability to shift all the bits clear off like what you expected.
 
OP
Toasty

Toasty

Sony battery
Sigh, my continued experimenting and subsequent Googling just lead me to the same conclusion. :( That's a bit of a downer as I'll have to throw in a bunch of checks everywhere now... unless someone knows of some really cool and ridiculously efficient way to overcome this behavior? :p
 

Exophase

Emulator Developer
Sigh, my continued experimenting and subsequent Googling just lead me to the same conclusion. :( That's a bit of a downer as I'll have to throw in a bunch of checks everywhere now... unless someone knows of some really cool and ridiculously efficient way to overcome this behavior? :p

What are you doing here? ARM emulation? For x86?

Putting in checks for it will usually be pretty efficient on an architecture that has conditional moves:

shr result, operand, cl
cmp cl, 31
cmovgt result, 0

Compiler's probably smart enough for this one.
 
Last edited:
OP
Toasty

Toasty

Sony battery
Actually it's not an emulation-related project. (The functions in question were processing video data to be placed in media files.) After thinking about it a bit more I decided I was over-complicating things and tossed out my old functions and rewrote it from scratch using a completely different method. Lo and behold, the simpler way was faster anyway. :rolleyes:

Thanks anyway though; at least now I know what was going wrong with my old code, and I'll know not to make the same assumption again.
 

Cyberman

Moderator
Moderator
In general go with the simplest method.

It is more reliable and working code is better than the nothing you gain otherwise. :D

It's been a while since I read a book on C/C++ ... gosh.. nothing has changed in 5 years in C++ (C# doesn't count ;) ).

Cyb
 

Top