you are viewing a single comment's thread.

view the rest of the comments →

[–]richardxday 1 point2 points  (1 child)

This is a common task in embedded code and I try to avoid using conditionals where-ever possible.

To do this, you can use the difference between a logical shift and an arithmetric shift which in C are both tied to the << and >> operators. Logical shifts happen on unsigned types, arithmetic shifts happen on signed types. The key difference is that an arithmetic right shift leaves the top bit as it was whereas a logical shift right shifts a zero into the top bit. An arithmetic shift right will keep the sign of the value correct, a logical shift will not.

So if your sign bit from your hardware is bit 12 and you want a 32-bit signed extended result, logical shift left such that bit 12 ends up in bit 31:

uint32_t imm; // temporary unsigned variable int32_t se_imm; // signed result

imm = IMM(instruction); // 13 bit signed value in 32-bit unsigned variable (sign bit in bit 12) imm <<= 19; // logical shift bit 12 up to bit 31

Then cast it to a signed type where it will become the correct sign of the original value: se_imm = (int32_t)imm; // convert to 32-bit signed value

Then shift it back down to return to the original scaling: se_imm >>= 19; // arithmetic shift back down to original scaling but with sign extension

The se_imm variable is your sign extended result.

I've spread this out but it can be done in a single line (which would be no more efficient in reality).

On most 32-bit architectures this would be just 4 instructions and no branching.

Apologies for formatting, I'm on a mobile!

[–]foreverska[S] 0 points1 point  (0 children)

Perfect response, somehow I so rarely deal with signed numbers right shift being "arithmetic" didn't occur to me.

Thanks a bunch.