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).