跳轉到內容

浮點/定點數字

來自華夏公益教科書

定點數字是一種簡單且易於表達分數的數字,使用固定數量的位數。沒有浮點硬體支援的系統經常使用定點數字來表示分數。(“沒有浮點硬體支援的系統”包括各種各樣的硬體——從高階定點 DSP、FPGA 和昂貴的定製 ASIC,它們處理流媒體的速度比任何浮點單元都快;到極低端的微控制器)。

二進位制點

[編輯 | 編輯原始碼]

術語“定點”指的是二進位制點的位置。二進位制點類似於十進位制數字的小數點,但由於這是二進位制而不是十進位制,因此使用不同的術語。在二進位制中,位可以是 0 或 1,並且沒有單獨的符號來指定二進位制點的位置。但是,我們想象或假設,二進位制點位於數字中指定位之間的一個固定位置。例如,在 32 位數字中,我們可以假設二進位制點存在於位 15(15 因為第一個位編號為 0,而不是 1)和 16 之間,為整數部分提供 16 位,為小數部分提供 16 位。請注意,整數字段中的最高有效位通常被指定為符號位,為整數的量級保留 15 位。

寬度和精度

[編輯 | 編輯原始碼]

定點數字的寬度是為定點數字分配的總位數。如果我們將整數部分和小數部分儲存在不同的儲存位置,則寬度將是該數字的總儲存量。定點數字的範圍是可能出現的最小數字和可能出現的最大數字之間的差。定點數字的精度是該數字小數部分的總位數。因為我們可以定義我們想要將固定二進位制點放置在何處,所以精度可以是任何數字,直到且包括數字的寬度。但是請注意,我們擁有的精度越高,我們擁有的總範圍就越小。

為了傳達用於表示整數和小數部分的位數,使用一種稱為Q 格式的符號。例如,Q5.2 表示整數部分是 5 位寬,小數部分是 2 位寬。

並非所有數字都能被定點數字精確表示,因此使用最接近的近似值。

計算 Qm.n 格式中浮點數 (x) 的整數表示 (X) 的公式為

X = round( x*2n )

要轉換回來,使用以下公式

x = X * 2-n

Q3.4 格式的一些示例

After conversion:                               After converting them back:
0100.0110 = 4 + 3/8                             4 + 3/8
0001.0000 = 1                                   1
0000.1000 =  1/2                                1/2
0000.0101 =  5/16                               0.3125
0000.0100 =  1/4                                1/4
0000.0010 =  1/8                                1/8
0000.0001 =  1/16                               1/16
0000.0000 = 0                                   0
1111.1111 = -1/16                               -1/16
1111.0000 = -1                                  -1
1100.0110 = -4 + 3/8 = -( 3 + 5/8 )             -4 + 3/8

隨機選擇的浮點數

0000.1011 = 0.673                               0.6875
0110.0100 = 6.234                               6.25

Q7.8 格式[1](非常常見)中的一些示例

0000_0001.0000_0000 = +1
1000_0001.0000_0000 = -127
0000_0000.0100_0000 = 1/4

由於二進位制點的位置完全是概念上的,因此新增和減去定點數字的邏輯與新增和減去整數所需的邏輯相同。因此,在 Q3.4 格式中將二分之一加二分之一時,我們期望看到

 0000.1000
+0000.1000
__________
=0001.0000

這等於 1,正如我們所期望的那樣。這同樣適用於減法。換句話說,當我們新增或減去定點數字時,和(或差)中的二進位制點將位於與我們操作的兩個數字中完全相同的位置。

當我們乘以兩個 8 位定點數字時,我們將需要 16 位來儲存乘積。顯然,由於結果中的位數與輸入不同,因此應預期二進位制點會移動。但是,它的工作原理與十進位制完全相同。

當我們在十進位制中乘以兩個數字時,小數點的位置在乘積的右端數字的左側 N 位,其中 N 是乘數和被乘數小數點右側的數字位數的總和。因此,在十進位制中,當我們乘以 0.2 乘以 0.02 時,我們得到

  0.2
x0.02
_____
0.004

乘數的小數點右側有一位,被乘數的小數點右側有兩三位。因此,乘積的小數點右側有三位(也就是說,小數點位於左側三位)。

它在二進位制中也是一樣的。

從上面的加法示例中,我們知道 Q3.4 格式中的二分之一等於十六進位制的 0x8。由於十六進位制中的 0x8 乘以 0x8 是十六進位制中的 0x0040,因此定點結果也可以預期為 0x0040——只要我們知道二進位制點的位置。讓我們用二進位制寫出乘積

0000000001000000

由於乘數和被乘數的二進位制點右側都有四位,因此乘積中二進位制點的位置在左側八位。因此,我們的答案是 00000000.01000000,正如我們所期望的那樣,等於四分之一。

如果我們希望輸出格式與輸入格式相同,則必須限制輸入範圍以防止溢位。將 Q7.8 轉換回 Q3.4 只需將乘積向右移 4 位即可。

FIR 濾波器

[編輯 | 編輯原始碼]

定點數字通常在數字濾波器(包括 FIR 和 IIR 濾波器)內部使用。

使用定點數字實現 FIR 和 IIR 演算法有一些實際考慮因素。[2][3]

正弦表

[編輯 | 編輯原始碼]

許多生成正弦波的嵌入式系統(如 DTMF 生成器)在程式記憶體中儲存一個“正弦表”。(它用於逼近數學正弦() 和餘弦() 函式)。由於此類系統通常的程式記憶體非常有限,因此當使用此類表時,通常以兩種不同的方式使用定點數字:儲存在表中的值以及用於索引這些表的“布拉德”。

儲存在正弦表中的值

[編輯 | 編輯原始碼]

通常,正弦和餘弦函式的一個象限被儲存在該表中。通常它是一個象限,在這個象限中,這些函式在 0 到 +1 的範圍內產生輸出值。這種表中的值通常儲存為定點數字——通常是 16 位無符號 Q0.16 格式的數字或 8 位無符號 Q0.8 值的數字。似乎有兩種流行的方法來處理 Q0.16 不能完全處理 1.0 的事實,它只能處理 0 到 (1.0-2^-16) 之間的數字: (a) 按正好是二的冪(在本例中為 2^16)進行縮放,就像大多數其他定點系統一樣,並將過大的值替換(裁剪)為可以儲存的最大值:因此 0 表示為 0,0.5 表示為 0x8000,(1.0-2^-16) 表示為 0xFFFF,1.0 被截斷並也表示為 0xFFFF。[4] (b) 按可能的最大值(在本例中為 0xFFFF)進行縮放,因此最大值和最小值都可以完全表示:因此 0 表示為 0,(1.0-2^-16) 表示為 0xFFFE,1.0 恰好表示為 0xFFFF。[5]

一些人用貝塞爾樣條曲線繪製相當精確的圓並計算相當精確的正弦和餘弦。該“表”成為 8 個值,代表一個單一的貝塞爾曲線,它以大約 4 ppm 的精度近似一個圓的 1/8,或以大約千分之一的精度近似一個圓的 1/4。[6][7]

一些使用極度受記憶體限制和速度緩慢的 8 位處理器的人使用一系列用定點算術計算的拋物線來計算正弦和餘弦的平滑近似值。[8] 該“表”成為一個非常簡單的公式,代表一個單一的拋物線。對於 0 到 90 度(以度為單位)的角度 A,

  • sin(A) ~= 1-((A-90)/90)^2,當 0 <= A <= 180 時,最大誤差小於 0.06
  • cos(A) ~= 1-(A/90)^2,當 -90 <= A <= 90 時,最大誤差小於 0.06

轉向

[edit | edit source]

許多人更喜歡用“圈數”來表示旋轉(例如角度)。“圈數”的整數部分表示發生了多少次完整旋轉。當用標準有符號定點算術乘以 360(或 1τ = 2π[9])時,“圈數”的小數部分會得到 -180 度(-π 弧度)到 +180 度(+π 弧度)範圍內的有效角度。在某些情況下,在二進位制角度上使用無符號乘法(而不是有符號乘法)很方便,這會得到 0 到 +360 度(+2π 弧度)範圍內的正確角度。

將角度儲存為圈數的定點分數的主要優點是速度。將某個“當前位置”角度與某個正或負的“增量角度”組合以獲得“新位置”速度非常快,即使在緩慢的 8 位微控制器上也是如此:它只需要進行一次“整數加法”,忽略溢位。其他儲存角度的格式需要相同的加法,加上處理 360 度溢位或 0 度下溢邊緣情況的特殊情況。

與將角度儲存在二進位制角度格式相比,將角度儲存在任何其他格式中——例如 360 度表示一個完整的旋轉,或 2π 弧度表示一個完整的旋轉——不可避免地會導致一些位模式產生超出該範圍的“角度”,需要額外的步驟將值範圍縮減到所需的範圍,或者會導致一些位模式根本不是有效角度(NaN),或者兩者都有。

在“圈數”單位中使用二進位制角度格式允許我們快速地(使用移位和掩碼,避免乘法)將位分離成

  • 表示整數圈數的位(在查詢角度的正弦時被忽略;有些系統根本不費心儲存這些位)
  • 表示象限的 2 位
  • 直接用於索引查詢表的位
  • 小於索引表中的一個“步長”的低位(相位累加器位,在沒有插值的情況下查詢角度的正弦時被忽略)

[10][11][12]

低位相位位即使沒有插值也能提高頻率解析度。

有些系統使用低位在表中的值之間進行線性插值。[13] 這樣一來,你就可以用更小的表獲得更高的精度(節省程式空間),代價是多花幾個週期來進行這個“額外”的插值計算。一些系統透過犧牲幾個週期來使用這些低位進行三次插值計算來獲得更高的精度,從而獲得更小的表。[4]

也許最常見的二進位制角度格式是“brad”。

許多嵌入式系統將角度(“圈數”的小數部分)儲存在單位元組二進位制角度格式中。[14] 有幾種解釋該位元組中值的方法,這些方法或多或少地意味著相同的角度

  • 以 brad(二進位制弧度)為單位的角度,儲存為 8 位無符號整數,從 0 到 255 brad
  • 以 brad 為單位的角度,儲存為 8 位有符號整數,從 -128 到 +127 brad
  • 以“圈數”為單位的角度,儲存為無符號 Q0.8 格式的圈數分數,從 0 到略小於 1 個完整的圈數
  • 以“圈數”為單位的角度,儲存為有符號 Q0.7 (?) 格式的圈數分數,從 -1/2 到略小於 +1/2 個完整的圈數

一個完整的圈數[15] 是 256 brad[16] 是 360 度。

如果一個位元組的精度不夠,brad 系統可以很容易地擴充套件到更多的小數位——每圈 65,536 個計數可以用 16 位表示。[17]

更多閱讀材料

[edit | edit source]

參考資料

[edit | edit source]
  1. Bruce R. Land. "GCC 和彙編中的定點數學函式".
  2. Randy Yates. "定點 FIR 濾波器實現中的實際考慮". 2010.
  3. Randy Yates. "定點算術:簡介". 2013.
  4. a b Brad Eckert:用 16 個等間距的 x 每 1/4 週期 三次樣條曲線 近似正弦“提供比 16 位精度更好的精度”sin(x)。[1]
  5. "重新規範化正弦表"
  6. Don Lancaster. "使用四個貝塞爾三次樣條曲線近似圓或橢圓。" "教程推導了從兩個到八個不同樣條擬合的近似數學方法。"
  7. 維基百科:貝塞爾樣條曲線#近似圓弧
  8. Tony P. "正弦脈衝 LED PWM 技巧:8 位方法".
  9. Michael Hartl. 陶數宣言:π 是錯誤的。 2010.
  10. 聲音合成理論/振盪器和波形表
  11. 使用波形表生成正弦波的描述
  12. Eric Smith. "在 PIC 上實現正弦函式"。 [2](沒有插值 - 速度快但粗糙)
  13. Scott Dattalo. "使用 PIC 生成正弦波"。 [3](使用插值 - 精度更高但速度更慢)
  14. Futurebasic/語言/參考/圓
  15. 維基百科:圈數(幾何)
  16. 維基百科:二進位制弧度
  17. Garth Wilson. "用於超快、精確、16 位定點/縮放整數數學的大型查詢表:三角學中的角度"。 [4]. 2012.
華夏公益教科書