I am doing some work in embedded C with an accelerometer that returns data as a 14 bit 2\'s complement number. I am storing this result directly into a uint16_t
Supposing that int
in your particular C implementation is 16 bits wide, the expression (1 << 15)
, which you use in mangling raw
, produces undefined behavior. In that case, the compiler is free to generate code to do pretty much anything -- or nothing -- if the branch of the conditional is taken wherein that expression is evaluated.
Also if int
is 16 bits wide, then the expression -(~raw + 1)
and all intermediate values will have type unsigned int
== uint16_t
. This is a result of "the usual arithmetic conversions", given that (16-bit) int
cannot represent all values of type uint16_t
. The result will have the high bit set and therefore be outside the range representable by type int
, so assigning it to an lvalue of type int
produces implementation-defined behavior. You'd have to consult your documentation to determine whether the behavior it defines is what you expected and wanted.
If you instead perform a 14-bit sign conversion, forcing the higher-order bits off ((~raw + 1) & 0x3fff
) then the result -- the inverse of the desired negative value -- is representable by a 16-bit signed int
, so an explicit conversion to int16_t
is well-defined and preserves the (positive) value. The result you want is the inverse of that, which you can obtain simply by negating it. Overall:
raw_signed = -(int16_t)((~raw + 1) & 0x3fff);
Of course, if int
were wider than 16 bits in your environment then I see no reason why your original code would not work as expected. That would not invalidate the expression above, however, which produces consistently-defined behavior regardless of the size of default int
.