If I subtract 2 numbers shifted left to a number and shift right, I get the original number. Example:
$$30 - 1 = ((30<<7) - (1<<7)) >> 7 = 29$$
or in other words
$$a-b = ((a<<s)-(b<<s))>>s$$
The $s$ is chosen carefully so $a<<s$ and $b<<s$ are so big that a small number $e$ added to $(a<<s)-(b<<s)$ won't interfere with the result. In theory, this small number would get added to the lower bits, which would disappear when shifted right again:
$$a-b = (e + (a<<s)-(b<<s))>>s $$
However, in practice, this does not happen:
$$ ((30<<7) - (1<<7)) >> 7 = 29\\ (-4 + (30<<7) - (1<<7)) >> 7 = 28 $$ why the results are different?
Your assumption that $e$ added to ... won't interfere" does not hold for negative $e$ because a negative number has (formally) infintely many set high bits when written in 2's complement. For example, take 8-bit numbers then
-2 = 11111110in 2's complement.
What you are trying to do is to manually form a bit-field where the lower $k$ bits encode $e$ and the upper $n-k$ bits encode $a-b$. You can do that, but you'll have to mask out the upper bits of $e$. When you are decoding the packed value, you'll have to restore the upper bits of $e$ again, i.e. you'll have to sign-extend the signed $k$-bit number $e$ to a signed $n$-bit number.
Here is an example output of a Python3 code. C/C++ would be mostly the same$^1$. $e$ is encoded in the lower 4 bits, and $ab$ is encoded in the upper 8 bits:
Notice how the higher bits are all '1''s for negative values.
The respective Python3 follows.
encodeencodeseinto the 4 LSBs andabinto the upper 8 bits. Asewill be encoded in signed 4-bit, it may range from $-8$ to $7$.decodedecodes the values and sign-extendseas needed.$^1$In C/C++ you might raise Undefined Behaviour when you shift a signed value left and it overflows. Hence, in C/C++ always operate on
unsignedvalues internally to avoid UB. But in C/C++ one would likely prefer bit-fields.Note: Instead of sign-extending by means of
e |= -1 << bits_eyou can also doe -= 1 << bits_eif preferred.