James Carter
James Carter

Reputation: 387

Sign-extend a number in C

I am having trouble trying to sign-extend a number by extracting part of a bit-string. This has trouble when it is a negative number, it wraps the number around to the positive side. Here is my code:

//  printf("add1 \n");
unsigned short r1 = (instruction>>6)&7;
signed short amount = (instruction& 31);   //right here! i am trying to get the last 5 bits and store it in a register but i can't figure out how to make it negative if it is negative
//  printf("\namount is %d \n", amount);
unsigned short dest = (instruction>>9)&7;
state->regs[dest] = state->regs[r1]+amount;
setCC(state,state->regs[r1]+amount);

Upvotes: 2

Views: 10985

Answers (5)

luser droog
luser droog

Reputation: 19504

For bit patterns, it's often easier to use hex constants instead of decimal.

signed short amount = (instruction & 0x1F);

Then to sign-extend the number, check the sign-bit (assuming the sign-bit here is the left-most of the 5 extracted bits). If it's set, do a binary inversion and add 1. Take the 2's-complement of the 5-bit value (invert and add one), then take the 2's-complement of the full-width result (invert and add 1).

if (amount & 0x10)
    amount = ~(amount^0x1F + 1) + 1;

Eg.

             5-bit "bitfield"
             X XXXX
0000 0000 0001 1111
0000 0000 0000 0000 invert x ^ 0x1F (= 1 1111)
0000 0000 0000 0001 add 1
1111 1111 1111 1110 invert ~
1111 1111 1111 1111 add 1

0000 0000 0001 0000
0000 0000 0000 1111 invert x ^ 0x1F (= 1 1111)
0000 0000 0001 0000 add 1
1111 1111 1110 1111 invert ~
1111 1111 1111 0000 add 1

Ooops. Even simpler:

-(x^0x1F + 1)  Assuming the machine operates with 2's-complement

0000 0000 0001 0110
0000 0000 0000 1001 invert
0000 0000 0000 1010 add 1 (yielding the full-width absolute value)
1111 1111 1111 0110 negate

Upvotes: 4

Mark Jeronimus
Mark Jeronimus

Reputation: 9543

From Hacker's Delight 2-6. Assuming 5 bits of data that must be sign extended (sign bit has value 16).

Best case: If the upper bits are all zeros:

(i ^ 16) - 16

Next best case (as with OP's instruction): If the upper bits contain data that must be discarded:

(i & 15) - (i & 16)

Upvotes: 2

chqrlie
chqrlie

Reputation: 144770

Here is how you can sign extend a 5-bit two's complement value portably without tests:

int amount = (instruction & 31) - ((instruction & 16) << 1);

More generally, it the field width is n, non zero and less than the number of bits in an int, you can write:

int amount = (instruction & ~(~1U << (n - 1) << 1)) -
             ((instruction & (1U << (n - 1)) << 1);

Upvotes: 1

anatolyg
anatolyg

Reputation: 28251

You can check the sign-bit and fix-up the result accordingly:

int width_of_field = 5;
signed short amount = (instruction& 31);
if (amount & (1 << width_of_field >> 1)) // look at the sign bit
{
    amount -= 1 << width_of_field; // fix the result
}

Alternatively, use a left-shift followed by a right shift:

width_of_field = 5;
signed short amount = (instruction& 31);
// It is possible to omit the "& 31", because of the left shift below
amount <<= 16 - width_of_field;
amount >>= 16 - width_of_field;

Note: must use two statements to avoid effects of promotion to int (which presumably has 32 bits).

Upvotes: 0

Aki Suihkonen
Aki Suihkonen

Reputation: 20027

use bitfields:

union {
      int a;
      struct {
         int a:5;
         int b:3;
         unsigned int c:20;
      } b;
} u = 0xdeadbeef;

int b = u.b.b;  // should sign extend the 3-bit bitfield starting from bit 5

Upvotes: 2

Related Questions