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?

GManNickG
  • 494,350
  • 52
  • 494
  • 543

15 Answers15

17

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

ars
  • 120,335
  • 23
  • 147
  • 134
8
unsigned char c;

c = ((c & 0xf0) >> 4) | ((c & 0x0f) << 4);
Ken Keenan
  • 9,818
  • 5
  • 32
  • 49
  • (see comment to Greg Hewgill's) ... rotate right. – KTC Jul 28 '09 at 08:08
  • 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 Jul 28 '09 at 08:25
8

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
  • 66,617
  • 42
  • 165
  • 277
  • I don't think that "this kind of interview question" is about a one correct solution but about how you approach and solve the problem. Part of your grading could even be that you asked the right questions before solving the problem. – hurikhan77 Jul 13 '20 at 01:37
3

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
  • 20,067
  • 28
  • 92
  • 110
  • 1
    No need for a temporary variable there, it can all be done in one expression. – Pavel Minaev Jul 28 '09 at 07:54
  • 1
    Why this fascination of doing everything in one expression? using a temporary variable is simple, readable and works. – Makach Jul 28 '09 at 07:56
  • The tmp var usually makes it more readable, and when it becomes a little bit more complicated.... – Johan Jul 28 '09 at 07:57
  • 1
    The one-liner is quite simple and readable. – sigjuice Jul 28 '09 at 07:58
  • 1
    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 Jul 28 '09 at 08:57
  • @Johan: Easy, do a `si` (step instruction) instead of a `step` and watch the EAX (or RAX) register using `display/x $eax`. – dreamlax Jul 28 '09 at 11:09
  • I was assuming Intel CPU, but you can still use `si` on other CPUs and watch other registers just as easily. – dreamlax Jul 28 '09 at 11:11
  • 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 Jul 28 '09 at 11:39
2

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
  • 951,095
  • 183
  • 1,149
  • 1,285
  • Rotate left ... (see comment to Ken Keenan's) – KTC Jul 28 '09 at 08:08
  • 1
    As some of the other comments suggested, the above only works on a 8-bit byte variable x. – KTC Jul 28 '09 at 08:24
  • @KTC: almost but not quite, you're on the right track though. – Greg Hewgill Jul 28 '09 at 08:26
  • 1
    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 Jul 28 '09 at 10:46
  • 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 Jul 28 '09 at 11:23
2

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
  • 167,383
  • 100
  • 513
  • 979
  • 1
    Um, is a standard C header, and have to define CHAR_BIT, which has to be at least be 8. – KTC Jul 28 '09 at 08:22
2
unsigned char b;
b = (b << 4) | (b >> 4);
Josh Lee
  • 171,072
  • 38
  • 269
  • 275
1

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)
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
Falaina
  • 6,625
  • 29
  • 31
1

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
  • 4,610
  • 5
  • 34
  • 47
  • 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 Jul 28 '09 at 08:19
  • you are right, the down vote is well deserved – Catalin DICU Jul 28 '09 at 08:26
  • Unless you care about original 8086 (immediate-count rotates were new in 186, IIRC), use `rol al, 4`. – Peter Cordes Aug 27 '18 at 14:24
0

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
  • 2,054
  • 3
  • 26
  • 40
0
/*swaping four bits*/
#include<stdio.h>

void printb(char a) {
    int i;
    for( i = 7; i >= 0; i--)
        printf("%d", (1 &  (a >> i)));
    printf("\n");
}

int swap4b(char a) {
    return ( ((a & 0xf0) >> 4) | ((a & 0x0f) << 4) );
}


int main()
{
    char a = 10;
    printb(a);
    a = swap4b(a);
    printb(a);
    return 0;
}
Megharaj
  • 1,589
  • 2
  • 20
  • 32
0

This is how you swap bits entirely, to change the bit endianess in a byte.

"iIn" is actually an integer because I'm using it to read from a file. I need the bits in an order where I can easily read them in order.

// swap bits
iIn = ((iIn>>4) & 0x0F) | ((iIn<<4) & 0xF0); // THIS is your solution here.
iIn = ((iIn>>2) & 0x33) | ((iIn<<2) & 0xCC);
iIn = ((iIn>>1) & 0x55) | ((iIn<<1) & 0xAA);

For swapping just two nibbles in a single byte, this is the most efficient way to do this, and it's probably faster than a lookup table in most situations.

I see a lot of people doing shifting, and forgetting to do the masking here. This is a problem when there is sign extension. If you have the type of unsigned char, it's fine since it's a unsigned 8 bit quantity, but it will fail with any other type.

The mask doesn't add overhead, with an unsigned char, the mask is implied anyhow, and any decent compiler will remove unnecessary code and has for 20 years.

user6269400
  • 129
  • 1
  • 2
0

Solution for generic n bits swapping between last and first. Not verified for case when total bits are less than 2n. here 7 is for char, take 31 for integer.

unsigned char swapNbitsFtoL(unsigned char num, char nbits)
{
    unsigned char u1 = 0;
    unsigned char u2 = 0;

    u1 = ~u1;
    u1 &= num;
    u1 = (u1 >> (7 - (nbits - 1))); /* Here nbits is number of n=bits so I have taken (nbits - 1). */

    u2 = ~u2;
    u2 &= num;
    u2 = (u2 << (7 - (nbits - 1))); /* Here nbits is number of n=bits so I have taken (nbits - 1). */

    u1 |= u2; /* u1 have first and last swapped n bits with */

    u2 = 0;
    u2 = ~u2;
    u2 = ((u2 >> (7 - (nbits - 1))) | (u2 << (7 - (nbits - 1))));
    bit_print(u2);
    u2 = ~u2;
    u2 &= num;

    return (u1 | u2);
}
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • bit_print(u2); is not defined – Anton Krug May 31 '21 at 21:29
  • you can easily get from github or any other website. Please google it. – Ashutosh Tiwari Apr 16 '23 at 18:12
  • My local google could give me different results, or I could find different fork of the reporsitory than you reference. If you depend on something, it would be good to state it clearly and explicitly as reference in your answer. Or then we could just give users answers such "google it" or "ask chatGPT". – Anton Krug Apr 23 '23 at 10:58
-1

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
  • 3,982
  • 1
  • 31
  • 50
  • 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 Jul 28 '09 at 09:58
-1
#include <stdio.h>
#include <conio.h>
#include <math.h>
void main() { 
    int q,t,n,a[20],j,temp;
    int i=0;
    int s=0;
    int tp=0;
    clrscr();
    printf("\nenter the num\n");
    scanf("%d",&n);
    t=n;
    while(n>0) {
        a[i]=n%2;
        i++;
        n=n/2;
    }
    printf("\n\n");
    printf("num:%d\n",t);
    printf("number in binary format:");
    for(j=i-1;j>=0;j--) {
        printf("%d",a[j]);
    }
    printf("\n");
    temp=a[i-1];
    a[i-1]=a[0];
    a[0]=temp;
    printf("number in binary format wid reversed boundary bits:");
    for(j=i-1;j>=0;j--) {
        printf("%d",a[j]);
    }
    printf("\n");
    q=i-1;
    while(q>=0) {
        tp=pow(2,q);
        s=s+(tp*a[q]);
        q--;
    }
    printf("resulatnt number after reversing boundary bits:%d",s);
    printf("\n");
    getch();
}
antyrat
  • 27,479
  • 9
  • 75
  • 76