Q (number format)
- 8 min read
- 3 chapters
Related on Lably
Through Two's complement and Rounding
A number of other notations have been used for the same purpose.
Definition
Texas Instruments version
The Q notation, as defined by Texas Instruments, consists of the letter Q followed by a pair of numbers m.n, where m is the number of bits used for the integer part of the value, and n is the number of fraction bits.
By default, the notation describes signed binary fixed point format, with the unscaled integer being stored in two's complement format, used in most binary processors. The first bit always gives the sign of the value(1 = negative, 0 = non-negative), and it is not counted in the m parameter. Thus, the total number w of bits used is 1 + m + n.
For example, the specification Q3.12 describes a signed binary fixed-point number with a w = 16 bits in total, comprising the sign bit, three bits for the integer part, and 12 bits that are the fraction. That is, a 16-bit signed (two's complement) integer, that is implicitly multiplied by the scaling factor 2−12
In particular, when n is zero, the numbers are just integers. If m is zero, all bits except the sign bit are fraction bits; then the range of the stored number is from −1.0 (inclusive) to +1.0 (exclusive).
The m and the dot may be omitted, in which case they are inferred from the size of the variable or register where the value is stored. Thus, Q12 means a signed integer with any number of bits, that is implicitly multiplied by 2−12.
The letter U can be prefixed to the Q to denote an unsigned binary fixed-point format. For example, UQ1.15 describes values represented as unsigned 16-bit integers with an implicit scaling factor of 2−15, which range from 0.0 to (216−1)/215 = +1.999969482421875.
ARM version
A variant of the Q notation has been in use by ARM. In this variant, the m number includes the sign bit. For example, a 16-bit signed integer would be denoted Q15.0 in the TI variant, but Q16.0 in the ARM variant.
Sign in to keep reading
2 more chapters are free with a Lably account.
Characteristics
The resolution (difference between successive values) of a Qm.n or UQm.n format is 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
<span id="mwoA"></span><span class="kt" id="mwoQ">int16_t</span><span class="w" id="mwog"> </span><span class="nf" id="mwow">q_add</span><span class="p" id="mwpA">(</span><span class="kt" id="mwpQ">int16_t</span><span class="w" id="mwpg"> </span><span class="n" id="mwpw">a</span><span class="p" id="mwqA">,</span><span class="w" id="mwqQ"> </span><span class="kt" id="mwqg">int16_t</span><span class="w" id="mwqw"> </span><span class="n" id="mwrA">b</span><span class="p" id="mwrQ">)</span> <span class="p" id="mwrg">{</span> <span class="w" id="mwrw"> </span><span class="k" id="mwsA">return</span><span class="w" id="mwsQ"> </span><span class="n" id="mwsg">a</span><span class="w" id="mwsw"> </span><span class="o" id="mwtA">+</span><span class="w" id="mwtQ"> </span><span class="n" id="mwtg">b</span><span class="p" id="mwtw">;</span> <span class="p" id="mwuA">}</span>With saturation
<span id="mwvA"></span><span class="kt" id="mwvQ">int16_t</span><span class="w" id="mwvg"> </span><span class="nf" id="mwvw">q_add_sat</span><span class="p" id="mwwA">(</span><span class="kt" id="mwwQ">int16_t</span><span class="w" id="mwwg"> </span><span class="n" id="mwww">a</span><span class="p" id="mwxA">,</span><span class="w" id="mwxQ"> </span><span class="kt" id="mwxg">int16_t</span><span class="w" id="mwxw"> </span><span class="n" id="mwyA">b</span><span class="p" id="mwyQ">)</span> <span class="p" id="mwyg">{</span> <span class="w" id="mwyw"> </span><span class="kt" id="mwzA">int16_t</span><span class="w" id="mwzQ"> </span><span class="n" id="mwzg">result</span><span class="p" id="mwzw">;</span> <span class="w" id="mw0A"> </span><span class="kt" id="mw0Q">int32_t</span><span class="w" id="mw0g"> </span><span class="n" id="mw0w">tmp</span><span class="p" id="mw1A">;</span> <span class="w" id="mw1Q"> </span><span class="n" id="mw1g">tmp</span><span class="w" id="mw1w"> </span><span class="o" id="mw2A">=</span><span class="w" id="mw2Q"> </span><span class="p" id="mw2g">(</span><span class="kt" id="mw2w">int32_t</span><span class="p" id="mw3A">)</span><span class="n" id="mw3Q">a</span><span class="w" id="mw3g"> </span><span class="o" id="mw3w">+</span><span class="w" id="mw4A"> </span><span class="p" id="mw4Q">(</span><span class="kt" id="mw4g">int32_t</span><span class="p" id="mw4w">)</span><span class="n" id="mw5A">b</span><span class="p" id="mw5Q">;</span> <span class="w" id="mw5g"> </span><span class="k" id="mw5w">if</span><span class="w" id="mw6A"> </span><span class="p" id="mw6Q">(</span><span class="n" id="mw6g">tmp</span><span class="w" id="mw6w"> </span><span class="o" id="mw7A">></span><span class="w" id="mw7Q"> </span><span class="mh" id="mw7g">0x7FFF</span><span class="p" id="mw7w">)</span> <span class="w" id="mw8A"> </span><span class="n" id="mw8Q">tmp</span><span class="w" id="mw8g"> </span><span class="o" id="mw8w">=</span><span class="w" id="mw9A"> </span><span class="mh" id="mw9Q">0x7FFF</span><span class="p" id="mw9g">;</span> <span class="w" id="mw9w"> </span><span class="k" id="mw-A">if</span><span class="w" id="mw-Q"> </span><span class="p" id="mw-g">(</span><span class="n" id="mw-w">tmp</span><span class="w" id="mw_A"> </span><span class="o" id="mw_Q"><</span><span class="w" id="mw_g"> </span><span class="mi" id="mw_w">-1</span><span class="w" id="mwAQA"> </span><span class="o" id="mwAQE">*</span><span class="w" id="mwAQI"> </span><span class="mh" id="mwAQM">0x8000</span><span class="p" id="mwAQQ">)</span> <span class="w" id="mwAQU"> </span><span class="n" id="mwAQY">tmp</span><span class="w" id="mwAQc"> </span><span class="o" id="mwAQg">=</span><span class="w" id="mwAQk"> </span><span class="mi" id="mwAQo">-1</span><span class="w" id="mwAQs"> </span><span class="o" id="mwAQw">*</span><span class="w" id="mwAQ0"> </span><span class="mh" id="mwAQ4">0x8000</span><span class="p" id="mwAQ8">;</span> <span class="w" id="mwARA"> </span><span class="n" id="mwARE">result</span><span class="w" id="mwARI"> </span><span class="o" id="mwARM">=</span><span class="w" id="mwARQ"> </span><span class="p" id="mwARU">(</span><span class="kt" id="mwARY">int16_t</span><span class="p" id="mwARc">)</span><span class="n" id="mwARg">tmp</span><span class="p" id="mwARk">;</span> <span class="w" id="mwARo"> </span><span class="k" id="mwARs">return</span><span class="w" id="mwARw"> </span><span class="n" id="mwAR0">result</span><span class="p" id="mwAR4">;</span> <span class="p" id="mwAR8">}</span>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
<span id="mwASQ"></span><span class="kt" id="mwASU">int16_t</span><span class="w" id="mwASY"> </span><span class="nf" id="mwASc">q_sub</span><span class="p" id="mwASg">(</span><span class="kt" id="mwASk">int16_t</span><span class="w" id="mwASo"> </span><span class="n" id="mwASs">a</span><span class="p" id="mwASw">,</span><span class="w" id="mwAS0"> </span><span class="kt" id="mwAS4">int16_t</span><span class="w" id="mwAS8"> </span><span class="n" id="mwATA">b</span><span class="p" id="mwATE">)</span> <span class="p" id="mwATI">{</span> <span class="w" id="mwATM"> </span><span class="k" id="mwATQ">return</span><span class="w" id="mwATU"> </span><span class="n" id="mwATY">a</span><span class="w" id="mwATc"> </span><span class="o" id="mwATg">-</span><span class="w" id="mwATk"> </span><span class="n" id="mwATo">b</span><span class="p" id="mwATs">;</span> <span class="p" id="mwATw">}</span>Multiplication
<span id="mwAUA"></span><span class="c1" id="mwAUE">// precomputed value:</span> <span class="cp" id="mwAUI">#define K (1 << (Q - 1))</span> <span class="w" id="mwAUM"> </span> <span class="c1" id="mwAUQ">// saturate to range of int16_t</span> <span class="kt" id="mwAUU">int16_t</span><span class="w" id="mwAUY"> </span><span class="nf" id="mwAUc">sat16</span><span class="p" id="mwAUg">(</span><span class="kt" id="mwAUk">int32_t</span><span class="w" id="mwAUo"> </span><span class="n" id="mwAUs">x</span><span class="p" id="mwAUw">)</span> <span class="p" id="mwAU0">{</span> <span class="w" id="mwAU4"> </span><span class="k" id="mwAU8">if</span><span class="w" id="mwAVA"> </span><span class="p" id="mwAVE">(</span><span class="n" id="mwAVI">x</span><span class="w" id="mwAVM"> </span><span class="o" id="mwAVQ">></span><span class="w" id="mwAVU"> </span><span class="mh" id="mwAVY">0x7FFF</span><span class="p" id="mwAVc">)</span><span class="w" id="mwAVg"> </span><span class="k" id="mwAVk">return</span><span class="w" id="mwAVo"> </span><span class="mh" id="mwAVs">0x7FFF</span><span class="p" id="mwAVw">;</span> <span class="w" id="mwAV0"> </span><span class="k" id="mwAV4">else</span><span class="w" id="mwAV8"> </span><span class="k" id="mwAWA">if</span><span class="w" id="mwAWE"> </span><span class="p" id="mwAWI">(</span><span class="n" id="mwAWM">x</span><span class="w" id="mwAWQ"> </span><span class="o" id="mwAWU"><</span><span class="w" id="mwAWY"> </span><span class="mh" id="mwAWc">-0x8000</span><span class="p" id="mwAWg">)</span><span class="w" id="mwAWk"> </span><span class="k" id="mwAWo">return</span><span class="w" id="mwAWs"> </span><span class="mh" id="mwAWw">-0x8000</span><span class="p" id="mwAW0">;</span> <span class="w" id="mwAW4"> </span><span class="k" id="mwAW8">else</span><span class="w" id="mwAXA"> </span><span class="k" id="mwAXE">return</span><span class="w" id="mwAXI"> </span><span class="p" id="mwAXM">(</span><span class="kt" id="mwAXQ">int16_t</span><span class="p" id="mwAXU">)</span><span class="n" id="mwAXY">x</span><span class="p" id="mwAXc">;</span> <span class="p" id="mwAXg">}</span> <span class="kt" id="mwAXk">int16_t</span><span class="w" id="mwAXo"> </span><span class="nf" id="mwAXs">q_mul</span><span class="p" id="mwAXw">(</span><span class="kt" id="mwAX0">int16_t</span><span class="w" id="mwAX4"> </span><span class="n" id="mwAX8">a</span><span class="p" id="mwAYA">,</span><span class="w" id="mwAYE"> </span><span class="kt" id="mwAYI">int16_t</span><span class="w" id="mwAYM"> </span><span class="n" id="mwAYQ">b</span><span class="p" id="mwAYU">)</span> <span class="p" id="mwAYY">{</span> <span class="w" id="mwAYc"> </span><span class="kt" id="mwAYg">int16_t</span><span class="w" id="mwAYk"> </span><span class="n" id="mwAYo">result</span><span class="p" id="mwAYs">;</span> <span class="w" id="mwAYw"> </span><span class="kt" id="mwAY0">int32_t</span><span class="w" id="mwAY4"> </span><span class="n" id="mwAY8">temp</span><span class="p" id="mwAZA">;</span> <span class="w" id="mwAZE"> </span><span class="n" id="mwAZI">temp</span><span class="w" id="mwAZM"> </span><span class="o" id="mwAZQ">=</span><span class="w" id="mwAZU"> </span><span class="p" id="mwAZY">(</span><span class="kt" id="mwAZc">int32_t</span><span class="p" id="mwAZg">)</span><span class="n" id="mwAZk">a</span><span class="w" id="mwAZo"> </span><span class="o" id="mwAZs">*</span><span class="w" id="mwAZw"> </span><span class="p" id="mwAZ0">(</span><span class="kt" id="mwAZ4">int32_t</span><span class="p" id="mwAZ8">)</span><span class="n" id="mwAaA">b</span><span class="p" id="mwAaE">;</span><span class="w" id="mwAaI"> </span><span class="c1" id="mwAaM">// result type is operand's type</span> <span class="w" id="mwAaQ"> </span><span class="c1" id="mwAaU">// Rounding; mid values are rounded up</span> <span class="w" id="mwAaY"> </span><span class="n" id="mwAac">temp</span><span class="w" id="mwAag"> </span><span class="o" id="mwAak">+=</span><span class="w" id="mwAao"> </span><span class="n" id="mwAas">K</span><span class="p" id="mwAaw">;</span> <span class="w" id="mwAa0"> </span><span class="c1" id="mwAa4">// Correct by dividing by base and saturate result</span> <span class="w" id="mwAa8"> </span><span class="n" id="mwAbA">result</span><span class="w" id="mwAbE"> </span><span class="o" id="mwAbI">=</span><span class="w" id="mwAbM"> </span><span class="n" id="mwAbQ">sat16</span><span class="p" id="mwAbU">(</span><span class="n" id="mwAbY">temp</span><span class="w" id="mwAbc"> </span><span class="o" id="mwAbg">>></span><span class="w" id="mwAbk"> </span><span class="n" id="mwAbo">Q</span><span class="p" id="mwAbs">);</span> <span class="w" id="mwAbw"> </span><span class="k" id="mwAb0">return</span><span class="w" id="mwAb4"> </span><span class="n" id="mwAb8">result</span><span class="p" id="mwAcA">;</span> <span class="p" id="mwAcE">}</span>Division
<span id="mwAcU"></span><span class="kt" id="mwAcY">int16_t</span><span class="w" id="mwAcc"> </span><span class="nf" id="mwAcg">q_div</span><span class="p" id="mwAck">(</span><span class="kt" id="mwAco">int16_t</span><span class="w" id="mwAcs"> </span><span class="n" id="mwAcw">a</span><span class="p" id="mwAc0">,</span><span class="w" id="mwAc4"> </span><span class="kt" id="mwAc8">int16_t</span><span class="w" id="mwAdA"> </span><span class="n" id="mwAdE">b</span><span class="p" id="mwAdI">)</span> <span class="p" id="mwAdM">{</span> <span class="w" id="mwAdQ"> </span><span class="cm" id="mwAdU">/* pre-multiply by the base (Upscale to Q16 so that the result will be in Q8 format) */</span> <span class="w" id="mwAdY"> </span><span class="kt" id="mwAdc">int32_t</span><span class="w" id="mwAdg"> </span><span class="n" id="mwAdk">temp</span><span class="w" id="mwAdo"> </span><span class="o" id="mwAds">=</span><span class="w" id="mwAdw"> </span><span class="p" id="mwAd0">(</span><span class="kt" id="mwAd4">int32_t</span><span class="p" id="mwAd8">)</span><span class="n" id="mwAeA">a</span><span class="w" id="mwAeE"> </span><span class="o" id="mwAeI"><<</span><span class="w" id="mwAeM"> </span><span class="n" id="mwAeQ">Q</span><span class="p" id="mwAeU">;</span> <span class="w" id="mwAeY"> </span><span class="cm" id="mwAec">/* Rounding: mid values are rounded up (down for negative values). */</span> <span class="w" id="mwAeg"> </span><span class="cm" id="mwAek">/* OR compare most significant bits i.e. if (((temp >> 31) & 1) == ((b >> 15) & 1)) */</span> <span class="w" id="mwAeo"> </span><span class="k" id="mwAes">if</span><span class="w" id="mwAew"> </span><span class="p" id="mwAe0">((</span><span class="n" id="mwAe4">temp</span><span class="w" id="mwAe8"> </span><span class="o" id="mwAfA">>=</span><span class="w" id="mwAfE"> </span><span class="mi" id="mwAfI">0</span><span class="w" id="mwAfM"> </span><span class="o" id="mwAfQ">&&</span><span class="w" id="mwAfU"> </span><span class="n" id="mwAfY">b</span><span class="w" id="mwAfc"> </span><span class="o" id="mwAfg">>=</span><span class="w" id="mwAfk"> </span><span class="mi" id="mwAfo">0</span><span class="p" id="mwAfs">)</span><span class="w" id="mwAfw"> </span><span class="o" id="mwAf0">||</span><span class="w" id="mwAf4"> </span><span class="p" id="mwAf8">(</span><span class="n" id="mwAgA">temp</span><span class="w" id="mwAgE"> </span><span class="o" id="mwAgI"><</span><span class="w" id="mwAgM"> </span><span class="mi" id="mwAgQ">0</span><span class="w" id="mwAgU"> </span><span class="o" id="mwAgY">&&</span><span class="w" id="mwAgc"> </span><span class="n" id="mwAgg">b</span><span class="w" id="mwAgk"> </span><span class="o" id="mwAgo"><</span><span class="w" id="mwAgs"> </span><span class="mi" id="mwAgw">0</span><span class="p" id="mwAg0">))</span><span class="w" id="mwAg4"> </span><span class="p" id="mwAg8">{</span><span class="w" id="mwAhA"> </span> <span class="w" id="mwAhE"> </span><span class="n" id="mwAhI">temp</span><span class="w" id="mwAhM"> </span><span class="o" id="mwAhQ">+=</span><span class="w" id="mwAhU"> </span><span class="n" id="mwAhY">b</span><span class="w" id="mwAhc"> </span><span class="o" id="mwAhg">/</span><span class="w" id="mwAhk"> </span><span class="mi" id="mwAho">2</span><span class="p" id="mwAhs">;</span><span class="w" id="mwAhw"> </span><span class="cm" id="mwAh0">/* OR shift 1 bit i.e. temp += (b >> 1); */</span> <span class="w" id="mwAh4"> </span><span class="p" id="mwAh8">}</span><span class="w" id="mwAiA"> </span><span class="k" id="mwAiE">else</span><span class="w" id="mwAiI"> </span><span class="p" id="mwAiM">{</span> <span class="w" id="mwAiQ"> </span><span class="n" id="mwAiU">temp</span><span class="w" id="mwAiY"> </span><span class="o" id="mwAic">-=</span><span class="w" id="mwAig"> </span><span class="n" id="mwAik">b</span><span class="w" id="mwAio"> </span><span class="o" id="mwAis">/</span><span class="w" id="mwAiw"> </span><span class="mi" id="mwAi0">2</span><span class="p" id="mwAi4">;</span><span class="w" id="mwAi8"> </span><span class="cm" id="mwAjA">/* OR shift 1 bit i.e. temp -= (b >> 1); */</span> <span class="w" id="mwAjE"> </span><span class="p" id="mwAjI">}</span> <span class="w" id="mwAjM"> </span><span class="k" id="mwAjQ">return</span><span class="w" id="mwAjU"> </span><span class="p" id="mwAjY">(</span><span class="kt" id="mwAjc">int16_t</span><span class="p" id="mwAjg">)(</span><span class="n" id="mwAjk">temp</span><span class="w" id="mwAjo"> </span><span class="o" id="mwAjs">/</span><span class="w" id="mwAjw"> </span><span class="n" id="mwAj0">b</span><span class="p" id="mwAj4">);</span> <span class="p" id="mwAj8">}</span>
