Q格式

Q格式是二進制的定點數格式，其中會標示小數位元（也可能包括整數位元）的長度。例如Q15數表示分數部份有15個位元，而Q1.14數表示1個整數位元以及14個小數位元。 針對有號的定點數，有二種Q格式的表示方式。其中一種是將符號位元算在整數位元裡，但另外一種就不是。例如，16位元的有號整數（無小數位元）可以表示為Q16.0或Q15.0。因此，在用說明Q格式的有號定點數字時，可能也需要一併標示總位元數（在此處為16）。 Q格式是幾種廣為使用的定點数中的一種。定點数常用在不支援浮點運算器的硬體,或是用在需要固定解析度的應用。 Q格式用來標示定點數，儲存以及運算都是以一般的二進位整數為基礎來處理，因此允許標準的整數硬體／算術邏輯單元來進行有理数運算。程式設計者可以依應用需求，選定其整數位元數、小數位元數（以及資料總位元數），而之後定點數的範圍以及解析度就會依整數位元數、小數位元數而不同。 有些DSP架構原生支持一些常用的Q格式（例如Q1.15），這種情形下，可以在一個指令下就進行四則運算，若是加減法，可以支援饱和运算，若是乘除法，可以有正規化的機能。不過大部份的DSP無此功能，若DSP架構不支持選定的Q格式，程式設計者就要自行再針對加減法進行饱和运算，針對乘除法進行正規化。 有號號Q格式的表示方式有兩種，都寫成Qm.n： Q表示這個數字是以Q格式表示，在德州仪器則用Q表示是有號的Q格式（Q是數學裡表示有理數的字母） m.（可省略，若省略的話，假設是0或1）是數字在用二補數表示下的整數位元數，可能包括符位元，也可能不包括（這也是若m省略的話，假設是0或1的原因）。 n是數字中小數的位元數，也就是二進位表示時，在小數點右邊的二進位個數（若n = 0，表示Q格式數字是整數）。 一種表示方式將符號位元放在整數位元 m中一起計算，另一種則不是。確認方式可以將m和n相加，看是否等於數字位元數，若相等，表示其符號位元放在整數位元 m，若比數字位元數少1，表示其其符號位元獨立計算。 此外，字母U可以放在Q前面，說明是無號數，例如UQ1.15，數值範圍是0.0 to +1.999969482421875 （也就是 1 + 2 15 − 1 2 15 {\displaystyle 1+{\frac {2^{15}-1}{2^{15}}}} ）。 有號的Q格式數值會用二補數儲存，正如大部份處理器處理整數的情形一樣。在二補數中，會用符號位元填滿沒有用到數字位元。 針對給定的Qm.n格式（假設符號位元也算在m個位元內），用m+n位元的有號整數來處理，再考慮其中有n位元是表示小數： 其範圍為 [ − ( 2 m − 1 ) , 2 m − 1 − 2 − n ] {\displaystyle [-(2^{m-1}),2^{m-1}-2^{-n}]} 其解析度為 2 − n {\displaystyle 2^{-n}} 針對給定的UQm.n格式，用m+n位元的有號整數來處理，再考慮其中有n位元是表示小數： 其範圍為 [ 0 , 2 m − 2 − n ] {\displaystyle [0,2^{m}-2^{-n}]} 其解析度為 2 − n {\displaystyle 2^{-n}} 例如Q15.1格式的數字： 需要位元數為15+1 = 16位元 其範圍為[-214, 214 - 2−1] = [-16384.0, +16383.5] = [0x8000, 0x8001 … 0xFFFF, 0x0000, 0x0001 … 0x7FFE, 0x7FFF] 其解析度為2−1 = 0.5 Q格式和浮点数不同，Q格式數字的解析度不隨數字大小而不冋。 若要將浮點數（例如IEEE 754）轉換為Qm.n的格式： 將浮點數乘以2n 四捨五入到最接近的整數 若要將Qm.nQ格式的數數轉換為浮點數： 將整數轉換為浮點數 乘以2−n Q格式的數字是二個整數的比例：會儲存分子，而分母固定是2n。 考慮以下的例子； Q8格式的分母是28 = 256 1.5等於384/256 會儲存384，256不儲存（因為是Q8格式，分母固定是256） 若Q格式的基底固定（n都相同），則Q格式的數學運算需維持分母不變。以下是二個相同Q格式數字 N 1 {\displaystyle N_{1}} 和 N 2 {\displaystyle N_{2}} 的運算。 N 1 d + N 2 d = N 1 + N 2 d N 1 d − N 2 d = N 1 − N 2 d ( N 1 d × N 2 d ) × d = N 1 × N 2 d ( N 1 d / N 2 d ) / d = N 1 / N 2 d {\displaystyle {\begin{aligned}{\frac {N_{1}}{d}}+{\frac {N_{2}}{d}}&={\frac {N_{1}+N_{2}}{d}}\\{\frac {N_{1}}{d}}-{\frac {N_{2}}{d}}&={\frac {N_{1}-N_{2}}{d}}\\\left({\frac {N_{1}}{d}}\times {\frac {N_{2}}{d}}\right)\times d&={\frac {N_{1}\times N_{2}}{d}}\\\left({\frac {N_{1}}{d}}/{\frac {N_{2}}{d}}\right)/d&={\frac {N_{1}/N_{2}}{d}}\end{aligned}}} 因為分母是二的次幂，因此和二的次幂相乘可以表示為二進制下的左移，和二的次幂相除可以表示為二進制下的右移，很多處理器的左移和右移會比乘除法要快。 為了要維持乘法和除法的精度，中間值需要用雙精度來儲存，在轉換回需要的Q格式數字之間，也需要先注意数值修约的處理。 兩個不同Q格式基底的數字也可以相乘除，相乘除的數字也可以用另一個基底表示。以下是二個Q格式數字 N 1 {\displaystyle N_{1}} （分母 d 1 {\displaystyle d_{1}} ）和 N 2 {\displaystyle N_{2}} （分母 d 2 {\displaystyle d_{2}} ）的運算，運算結果的分母是 d 3 {\displaystyle d_{3}} 。 ( N 1 d 1 × N 2 d 2 ) × d 1 d 2 d 3 = N 1 × N 2 d 3 ( N 1 d 1 / N 2 d 2 ) × d 1 d 2 d 3 = N 1 / N 2 d 3 {\displaystyle {\begin{aligned}\left({\frac {N_{1}}{d_{1}}}\times {\frac {N_{2}}{d_{2}}}\right)\times {\frac {d_{1}d_{2}}{d_{3}}}&={\frac {N_{1}\times N_{2}}{d_{3}}}\\\left({\frac {N_{1}}{d_{1}}}/{\frac {N_{2}}{d_{2}}}\right)\times {\frac {d_{1}}{d_{2}d_{3}}}&={\frac {N_{1}/N_{2}}{d_{3}}}\end{aligned}}} 若用C语言，相同Q格式基底數字四則運算對應的程式如下（以下的Q是表示小數部份的位元數） int16_t q_add(int16_t a, int16_t b) { return a + b; } 有飽和 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; } 浮點數有±Inf，但Q格式沒有，若不進行飽和處理，二個很大的正數相加，可能會變成一個很大的負數。若是用組合語言，可以用Signed Overflow旗標來避免C語言實現時需要的型態轉換。 int16_t q_sub(int16_t a, int16_t b) { return a - b; } // 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 q_mul(int16_t a, int16_t b) { int16_t result; int32_t temp; temp = (int32_t)a * (int32_t)b; // result type is operand's type // Rounding; mid values are rounded up temp += K; // Correct by dividing by base and saturate result result = sat16(temp >> Q); return result; } int16_t q_div(int16_t a, int16_t b) { /* pre-multiply by the base (Upscale to Q16 so that the result will be in Q8 format) */ int32_t temp = (int32_t)a << Q; /* Rounding: mid values are rounded up (down for negative values). */ /* OR compare most significant bits i.e. if (((temp >> 31) & 1) == ((b >> 15) & 1)) */ if ((temp >= 0 && b >= 0) || (temp < 0 && b < 0)) { temp += b / 2; /* OR shift 1 bit i.e. temp += (b >> 1); */ } else { temp -= b / 2; /* OR shift 1 bit i.e. temp -= (b >> 1); */ } return (int16_t)(temp / b); } 二進位標度（英语：Binary scaling） 定點數運算 浮點數運算（英语：Floating-point arithmetic） ARM Developer Suite AXD and armsd Debuggers Guide. 1.2. 安謀控股. Chapter 4.7.9. AXD > AXD Facilities > Data formatting > Q-format. 2001 [1999]. ARM DUI 0066D. （原始内容存档于2017-11-04）.  Chapter 4.7.9. AXD > AXD Facilities > Data formatting > Q-format. RealView Development Sui (PDF). 安謀控股. 2006: 4–24 [1999]. ARM DUI 0066G. （原始内容存档 (PDF)于2017-11-04）.  Oberstar, Erick L. Fixed Point Representation & Fractional Math (PDF). 1.2. Oberstar Consulting. 2007-08-30 [2004] [2017-11-04]. （原始内容存档 (PDF)于2017-11-04）.  (Note: the accuracy of the article is in dispute; see discussion.) Q-Number-Format Java Implementation. [2017-11-04]. （原始内容存档于2017-11-04）.