views:

1859

answers:

11

In a C interview, I was asked to swap the first 4-bits of a number with the last 4 bit. (eg. 1011 1110 should be 1110 1011.)

Does anyone have a solution for this?

+1  A: 

Just use a temporary variable and move the last bit into that variable, then shift the bit in that direction and end of masking in the bits in the tmp var and you are done.


Update: Let's add some code and then you can choose what is more readable.

The working one liner

unsigned int data = 0x7654;
data = (data ^ data & 0xff) | ((data & 0xf) << 4) | ((data & 0xf0) >> 4);
printf("data %x \n", data);


the same code but with some tmp vars

unsigned int data = 0x7654;

unsigned int tmp1 = 0;
unsigned int tmp2 = 0;

tmp1 = (0x0f&data)<<4;
tmp2 = (0xf0&data)>>4;
tmp1 = tmp1 | tmp2;

data = data ^ (data & 0xff); 

data = data | tmp1;

printf("data %x \n", data);


Well the one liner is shorter anyway :)


Update:

And if you look at the asm code that gcc generated with -Os -S, my guess is that they are more or less identical since the overhead is removed during the "compiler optimisation" part.

Johan
No need for a temporary variable there, it can all be done in one expression.
Pavel Minaev
Why this fascination of doing everything in one expression? using a temporary variable is simple, readable and works.
Makach
The tmp var usually makes it more readable, and when it becomes a little bit more complicated....
Johan
The one-liner is quite simple and readable.
sigjuice
But step the code in gdb and see how easy it is to debug a one liner.... one liners are nice when they work :)
Johan
@Johan: Easy, do a `si` (step instruction) instead of a `step` and watch the EAX (or RAX) register using `display/x $eax`.
dreamlax
I was assuming Intel CPU, but you can still use `si` on other CPUs and watch other registers just as easily.
dreamlax
This doesn't swap the first 4 with the last 4, it swaps the first (or last, depending which way you're facing) with the next 4.
Steve Jessop
+1  A: 

There's no need for a temporary variable, something like this should do it:

x = ((x & 0xf) << 4) | ((x & 0xf0) >> 4);

There is a potential pitfall with this depending on the exact type of x. Identification of this problem is left as an exercise for the reader.

Greg Hewgill
Rotate left ... (see comment to Ken Keenan's)
KTC
As some of the other comments suggested, the above only works on a 8-bit byte variable x.
KTC
@KTC: almost but not quite, you're on the right track though.
Greg Hewgill
What Greg's on about is that it doesn't necessarily work if x is signed and has a negative value, since the right shift may then be arithmetic instead of logical.
Steve Jessop
Hang on, I take it back. The mask with 0xf0 promotes to int if int is at least 17 bits, or unsigned int if int is only 16 bits. Either way, the shift will then be logical, because either the sign bit of x is removed by the mask, or else the value being shifted is of unsigned type.
Steve Jessop
+5  A: 
unsigned char c;

c = ((c & 0xf0) >> 4) | ((c & 0x0f) << 4);
Ken Keenan
(see comment to Greg Hewgill's) ... rotate right.
KTC
As some of the other comments suggested, the above only works on a 8-bit char. (C guarantee / define char to be a byte, which has to be at least 8-bits, but can be more.)
KTC
A: 

Are you looking for something more clever than standard bit-shifting?

(assuming a is an 8-bit type)

a = ((a >> 4) & 0xF) + ((a << 4) &0xF0)

Falaina
+2  A: 

C++-like pseudocode (can be easily rewritten to not use temporary variables):

int firstPart = source & 0xF;
int offsetToHigherPart = sizeof( source ) * CHAR_BIT - 4;
int secondPart = ( source >> offsetToHigherPart ) & 0xF;
int maskToSeparateMiddle = -1 & ( ~0xF ) & ( ~( 0xF << offsetToHigherPart );
int result = ( firstPart << offsetToHigherPart ) | secondPart | (source & maskToSeparateMiddle);

This will require CHAR_BIT to be defined. It is usually in limits.h and is defined as 8 bits but is strictly speaking platform-dependent and can be not defined at all in the headers.

sharptooth
Um, <limits.h> is a standard C header, and have to define CHAR_BIT, which has to be at least be 8.
KTC
+1  A: 

x86 assembly:

asm{
  mov AL, 10111110b
  rol AL
  rol AL
  rol AL
  rol AL
}

http://www.geocities.com/SiliconValley/Park/3230/x86asm/asml1005.html

Catalin DICU
I do like this "out of the box"-thinking of stepping away from the question and providing an answer that is not quite answering the question but probably providing a valuable solution. Nonetheless, in doing so, one should leave additional information, like "this is not wise if you intend to write C code that compiles on different processor families" or "and you have to encapsulate it in the following statement for it to become C". Otherwise answering a C-question with assembler is not exactly correct.
Don Johe
you are right, the down vote is well deserved
Catalin DICU
+4  A: 

If you haven't seen or done much bit twiddling, a good resource to study is:

ars
Beat me to it, I was going to suggest the same. :)
arke
A: 

My skills in this area are new and therefore unproven so if I'm wrong then I learn something new, which is at least a part of the point of Stack Overflow.

Would a bitmask and XOR work also?

Like so?

var orginal=
var mask =00001110 //I may have the mask wrong
var value=1011 1110
var result=value^mask;

I might be misunderstanding things, forgive me if I've screwed up entriely.

Crippledsmurf
No, it wouldn't work, as the swap must MOVE bits, and bitwise operations MUST treat each bit independently of all others. (that's why they're called "bitwise") However, it's a common misunderstanding, particularly because the term "swapping bits" is occasionally used to mean "inverting bits" - which XOR is right for.
Roddy
+4  A: 

There is no "correct answer" to this kind of interview question. There are several ways to do this (lookup tables, anyone?) and the tradeoffs between each way (readability vs. performance vs. portability vs. maintainability) would need to be discussed.

The question is just an opening gambit to get you discussing some of the above issues, and to determine how 'deeply' you can discuss such problems.

Roddy
+1  A: 
unsigned char b;
b = (b << 4) | (b >> 4);
A: 

The easiest is (t is unsigned):

t = (t>>4)|(t<<4);

But if you want to obfuscate your code, or to swap other bits combination you can use this base:

mask = 0x0F & (t ^ (t >> 4));
t ^= (mask | (mask << 4));
zxcat