Sign extend a nine-bit number in C

(instr & 0x1FF) * (1 - ((unsigned short)(instr & 0x100) >> 7))

How does it work? It selects your sign bit and shifts it to the 2's position. This is used to generate either the value 1 (if your sign bit was absent) or -1 (if your sign bit was present).

This solution is branchless and does not depend on undefined behavior.


* No branching required *

See http://graphics.stanford.edu/~seander/bithacks.html#FixedSignExtend for a list of very useful bit hacks. Specifically, sign extending a number is as simple as:

/* generate the sign bit mask. 'b' is the extracted number of bits */
int m = 1U << (b - 1);  

/* Transform a 'b' bits unsigned number 'x' into a signed number 'r' */
int r = (x ^ m) - m; 

You may need to clear the uppermost bits of 'x' if they are not zero ( x = x & ((1U << b) - 1); ) before using the above procedure.

If the number of bits 'b' is known at compile time (e.g., 5 bits in your case) there's even a simpler solution (this might trigger a specific sign-extend instruction if the processor supports it and the compiler is clever enough):

struct {signed int x:5;} s;
r = s.x = x;

Assuming a short is 16 bits:

You can do it manually: (instr & 0x1FF) | ((instr & 0x100) ? 0xFE00 : 0). This tests the sign bit (the uppermost bit you are retaining, 0x100) and sets all the bits above it if the sign bit is set. You can extend this to 5 bits by adapting the masks to 0x1F, 0x10 and 0xFFE0, being the lower 5 bits, the 5th bit itself and all the bits 5-16 respectively.

Or you can find some excuse to assign the bits to the upper part of a signed short and shift them down (getting a sign-extension in the process): short x = (instr & 0x1FF) << 7; x >>= 7; The latter may actually end up being more straightforward in assembly and will not involve a branch. If instr is signed this can be done in a single expression: (instr & 0x1FF) << 7 >> 7. Since that already removes the upper bits it simplifies to instr << 7 >> 7. Replace 7 with 11 for 5 bits (16-5).

Tags:

C