Say we have an 8-bit A/D and we bias the input to half rail and capacitively couple the ground referenced input signal. The signed digital output with no signal is theoretically either 01111111 or 10000000.
When the input signal swings positive the signed digital value is a negative number and negative signal gives a positive number. Does that make any sense at all?
Your A/D yields unsigned 8-bit integer samples, but you're interpreting them as signed (two's complement) 8-bit integers.
The two most common options is to either treat the samples as unsigned integers, or compensate for the two's complement.
If you treat the samples as unsigned integers, zero input will correspond to sample 127 or 128, maximally negative input to sample 0, and maximally positive input to sample 127.
If you
convert the samples to signed integers by subtracting the bias, zero input will correspond to sample 0, maximally negative input to sample -128, and maximally positive input to 127.
If your hardware uses two's complement form – basically all that support C; and if it supports
uintN_t and
intN_t types in C, it definitely does so for these types –, you can compensate by unconditionally flipping the highest bit. I.e,
int8_t sample = read_uint8_t_sample() - 128;where
read_uint8_t_sample() returns an
int,
unsigned int, or
uint8_t (but not
int8_t), and
128 is the nearest power of two (2
8-1=2
7=128) to your bias.
Note that since
int8_t range is from -128 to 127, inclusive, you can write the subtraction as
+ (-128) in other languages. In C, it does not matter, because of
integer promotions: whenever a logical or arithmetic operation is applied, all types smaller than
int are converted to
int (or to
unsigned int, if
int cannot describe all possible values but
unsigned int can), and so on.
However, because the value affected is just the most significant bit – it's state will be flipped! –, any of the following will yield the exact same effect:
int8_t sample = read_uint8_t_sample() + 128; int8_t sample = read_uint8_t_sample() - 128; int8_t sample = read_uint8_t_sample() ^ 128; int8_t sample = read_uint8_t_sample() ^ (-128); int8_t sample = (int8_t)read_uint8_t_sample() + (int8_t)-128; int8_t sample = (int8_t)read_uint8_t_sample() - (int8_t)-128; int8_t sample = (int8_t)read_uint8_t_sample() ^ 128; int8_t sample = (int8_t)read_uint8_t_sample() ^ (int8_t)(-128);Of these, only the exclusive-OR (
^) ones do not rely on wraparound (modulo) arithmetic.
Since C only specifies for unsigned integer types, the exclusive-OR one is the "best" option: least surprise and possibility of implementation differences.
That's the real reason some use the exclusive-OR; it is not about "speed" or efficiency at all, just a portability/C technicality thing.