This is actually pretty complicated (if you haven't done it before) and there isn't enough space here to do go through it thoroughly. Start here https://en.wikipedia.org/wiki/Q_(number_format)
Quick (simplified) overview: We use 8 bit unsigned numbers as an example. Signed makes it more complicated.
When you multiply two integers you basically get twice the number of bits. The result of an 8 bit number multiplied with another 8 bit number requires 16 bit for accurate representation. So after a few multiples you end up with hundreds of bits which simply doesn't work. So you need to decide which bits to keep and which ones to throw away.
The Q notation is a bookkeeping process to help with this. You assume that somewhere there is a decimal point. "Q" denotes where the decimal point is. Q=0 means all the way to the right, Q=8 means all the way to left. To convert from integer to floating point you simply multiply by $2^{-Q}$.
The largest number you can represent this way is 255 for Q0, $\frac{255}{256} = 0.966$ for Q8, and $\frac{255}{16} = 15.9375$ for Q4. The smallest non zero number you can represent is 1 for Q0, $\frac{1}{256} = 0.004$ for Q8 and $\frac{1}{16} = 0.0625$ for Q4. So you basically trading the absolute range against resolution.
Let's say you want to represent the number 2.5. You can't do this in Q0 (you could chose 2 or 3 but there is nothing in between) and you can't do it in Q8 either since it's larger than 1. Q1, however would work. 0b00000101 in Q1 = 2.5 Q1. Q3 would work as well: 0b00010100 in Q3 = 2.5. So any floating point number can be represented as different bit pattern, depending on what Q you chose.
If you want the same floating point number in a different Q you need to shift. shifting. Shifting to the left increases Q, shifting to the right decreases Q.
In Q8 the largest number you can represent is smaller than 1. That means it's impossible that any multiplication would give a result that's larger than any of its inputs and hence it can never overflow. To produce an 8 bit result you keep the MSBs of the 16 bit result and throw away the LSBs.
For a Q0 multiply you keep the LSB and throw away the MSBs. This can and will overflow. 2*2 is 4 but 16*200 will overflow since the result cannot be represented in 8 bits.
If you multiply two numbers, both the number of bits and the Qs will add. So if you multiply an 8-bit Q6 with an 8-bit Q5, you get a 16-bit Q11. To convert 16 bit back to 8 bit you have to throw some bits away. Throwing away LSBs (least significant bits) reduces the number of bits and the Q by the same amount. In our example, removing the lower 8 bits and keeping the upper 8 bits would result in an 8-bit Q3.
So what does this all mean in practice? It basically determines how you implement multiplies and which bits you keep. In general a multiply looks like this
- Multiply 2 N bit numbers into a 2N wide accumulator or register
- Shift the result depending on the desired output $Q_C$. $S = Q_C - (Q_A + Q_B - N)$.
- Keep the upper N bits and transfer to result variable
The tricky part here is to figure which Q to chose for each variable. If the Q is too low, you can overflow and if it's too high you can get very bad signal to noise ratio. Often this requires careful analysis of the specific algorithm and the properties of the input signals.
sample=((byte1<<24)|(byte2<<16)|(byte3<<8))>>8;
why are you shifting the whole thing to the right by 8 bits? why aren't you leaving the sample words left justified? and if you want it right justified why not leave out 8 bits of unnecessary shifting:sample=(byte1<<16)|(byte2<<8)|byte3;
are you doing that for the purpose of sign extension? $\endgroup$ – robert bristow-johnson May 29 '16 at 1:02