always 2−n. The range of representable values depends on the notation used: For example, a Q15.1 format number requires 15+1 = 16 bits, has resolution 2−1 = 0.5, and the representable values range from −214 = −16384.0 to +214 − 2−1 = +16383.5. In hexadecimal, the negative values range from 0x8000 to 0xFFFF followed by the non-negative ones from 0x0000 to 0x7FFF. Math operations Q numbers are a ratio of two integers: the numerator is kept in storage, the denominator is equal to 2n. Consider the following example: The Q8 denominator equals 28 = 256 1.5 equals 384/256 384 is stored, 256 is inferred because it is a Q8 number. If the Q number's base is to be maintained (n remains constant) the Q number math operations must keep the denominator constant. The following formulas show math operations on the general Q numbers and . (If we consider the example as mentioned above, is 384 and is 256.) Because the denominator is a power of two, the multiplication can be implemented as an arithmetic shift to the left and the division as an arithmetic shift to the right; on many processors shifts are faster than multiplication and division. To maintain accuracy, the intermediate multiplication and division results must be double precision and care must be taken in rounding the intermediate result before converting back to the desired Q number. Using C the operations are (note that here, Q refers to the fractional part's number of bits) : Addition int16_t q_add(int16_t a, int16_t b) { return a + b; } With saturation int16_t q_add_sat(int16_t a, int16_t b) { int16_t result; int32_t tmp; tmp = (int32_t)a + (int32_t)b; if (tmp > 0x7FFF) tmp = 0x7FFF; if (tmp < -1 * 0x8000) tmp = -1 * 0x8000; result = (int16_t)tmp; return result; } Unlike floating point ±Inf, saturated results are not sticky and will unsaturate on adding a negative value to a positive saturated value (0x7FFF) and vice versa in that implementation shown. In assembly language, the Signed Overflow flag can be used to avoid the typecasts needed for that C implementation. Subtraction int16_t q_sub(int16_t a, int16_t b) { return a - b; } Multiplication // precomputed value: #define K (1 << (Q - 1)) // saturate to range of int16_t int16_t sat16(int32_t x) { if (x > 0x7FFF) return 0x7FFF; else if (x < -0x8000) return -0x8000; else return (int16_t)x; } int16_t