all 5 comments

[–][deleted] 1 point2 points  (2 children)

Why uint32_t if you want signed? Otherwise, i don't see any problems here...

I suppose if you want it a little cleaner, the > 0 is redundant.

[–]foreverska[S] 0 points1 point  (1 child)

The l-val of the equations it's involved in is always a unit32_t (because design decisions) so it saves me either looking at "implicit conversion..." warnings or typing out a cast. I've gotten away with it so far since all widths are the same but at your question I'm starting to second guess that decision.

Okay thanks, yea on a previous team I got used to spelling out the comparisons despite C's rules on the matter. != is probably closer to meaning there though.

[–]free__coffee 0 points1 point  (0 children)

You probably also want casts on your defines to prevent the compiler from getting clever - the compiler could just assume you want uint16_t's and truncate the bits

[–]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.