浮點(diǎn)算法不遵循整數(shù)算法規(guī)則,但利用 FPGA 或者基于 FPGA 的嵌入式處理器不難設(shè)計(jì)出精確的浮點(diǎn)系統(tǒng)。
工程人員一看到浮點(diǎn)運(yùn)算就會頭疼,因?yàn)楦↑c(diǎn)運(yùn)算用軟件實(shí)現(xiàn)速度慢,用硬件實(shí)現(xiàn)則占用資源多。理解和領(lǐng)會浮點(diǎn)數(shù)的最佳方法是將它們視為實(shí)數(shù)的近似值。實(shí)際上這也是開發(fā)浮點(diǎn)表達(dá)式的目的。正如老話所說,真實(shí)的世界是模擬的,有許多電子系統(tǒng)在與真實(shí)世界的信號打交道。因此,對于設(shè)計(jì)這些系統(tǒng)的工程師來說,有必要理解浮點(diǎn)表達(dá)式以及浮點(diǎn)計(jì)算的優(yōu)勢和局限性。這將有助于我們設(shè)計(jì)可靠性更高、容錯(cuò)性更好的系統(tǒng)。
首先深入了解一下浮點(diǎn)運(yùn)算。通過一些示例計(jì)算就可以看到浮點(diǎn)運(yùn)算不如整數(shù)運(yùn)算直接,工程師在設(shè)計(jì)使用浮點(diǎn)數(shù)據(jù)的系統(tǒng)時(shí)必須考慮這一點(diǎn)。這里有個(gè)重要的技巧:用對數(shù)來運(yùn)算極小的浮點(diǎn)數(shù)據(jù)。我們的目的是熟悉一些數(shù)值運(yùn)算的特點(diǎn),把重點(diǎn)放在設(shè)計(jì)問題上。本文結(jié)尾列出的參考文獻(xiàn)中有更加深入的介紹。
就設(shè)計(jì)實(shí)現(xiàn)而言, 設(shè)計(jì)人員可以使用賽靈思LogiCORE? IP Floating-Point Operator 生成和實(shí)例化RTL 設(shè)計(jì)中的浮點(diǎn)運(yùn)算符。如果采用 FPGA 中的嵌入式處理器構(gòu)建浮點(diǎn)軟件系統(tǒng),則可以使用 Virtex-5 FXT 中面向PowerPC 440 嵌入式處理器的 LogicCORE IP Virtex?-5輔助處理器單元 (APU) 浮點(diǎn)單元 (FPU)。
浮點(diǎn)運(yùn)算 101
IEEE 754-2008 是現(xiàn)行的浮點(diǎn)運(yùn)算 IEEE 標(biāo)準(zhǔn)。(1)它取代了該標(biāo)準(zhǔn)的 IEEE 754-1985 和 IEEE 754-1987 版本。該標(biāo)準(zhǔn)中的浮點(diǎn)數(shù)據(jù)表達(dá)規(guī)則如下:
? 要求零帶符號:+0,-0
? 非零浮點(diǎn)數(shù)可表達(dá)為 (-1)^s x b^e x m,這里:s 為 +1 或者 -1,用于表明該數(shù)為正數(shù)還是負(fù)數(shù),b是底數(shù)(2 或者 10),e 是指數(shù),m 則是以 d0.d1d2—dp-1 形式表達(dá)的數(shù),這里 di 對以 2 為底數(shù)的情況可以是 0 或者 1,對以 10 為底數(shù)的情況可以是0 和 9 之間的任意值。請注意小數(shù)點(diǎn)應(yīng)緊跟 d0。
? 分正負(fù)的極限:+∞,-∞。
? 非數(shù)值,有兩種形式:qNaN(靜態(tài))和 sNaN(信號)。
表達(dá)式 d0.d1d2—dp-1 指“有效值”,e 為“指數(shù)”。有效值總共有 p 位數(shù),p 即為表達(dá)式的精度。IEEE754-2008 定義了五種基本的表達(dá)式格式,三種用于 2 為底數(shù)的情況,兩種用于 10 為底數(shù)的情況。標(biāo)準(zhǔn)中還提供更多的衍生格式。IEEE 754-1985 中規(guī)定的單精度浮點(diǎn)數(shù)和雙精度浮點(diǎn)數(shù)分別稱為 binary32 和 binary64。對每種格式都規(guī)定有最小的指數(shù) emin 和最大的指數(shù) emax。
可以用浮點(diǎn)數(shù)格式表達(dá)的有限值的范圍取決于底數(shù)(b)、精度 (p) 和 emax。該標(biāo)準(zhǔn)一般將 emin 定義為1-emax:
? m 是 0 到 b^p-1 內(nèi)的整數(shù)
例如,在 p=10 和 b=2 的情況下,m 就介于 0 到1023 之間。
? 對給定的數(shù),e 必須滿足下式
1-emax<=e+p-1<=emax
例如,如果 p=24,emax=+127,則 e 的取值范圍是-126 到 104。
這里需要清楚一點(diǎn),即浮點(diǎn)表達(dá)式往往不是唯一的。
例如,0.02x10^1 和 2.00x10^(-1) 都代表相同的實(shí)數(shù),即0.2。如果第一位數(shù) d0 為 0,則稱該數(shù)值被“標(biāo)準(zhǔn)化”。同時(shí),對某個(gè)實(shí)數(shù)來說,可能不存在浮點(diǎn)表達(dá)式。例如,0.1 在十進(jìn)制中是一個(gè)確定的值,但它的二進(jìn)制表達(dá)式是一個(gè)小數(shù)點(diǎn)后 0011 的無窮循環(huán)。因此,0.1 無法用浮點(diǎn)格式確定地表達(dá)。表 1 給出了 IEEE 754-2008 規(guī)定的五種基本格式。
浮點(diǎn)計(jì)算的誤差
因?yàn)橐霉潭ㄎ粩?shù)來表達(dá)無窮數(shù)量的實(shí)數(shù),因此在浮點(diǎn)計(jì)算中四舍五入是必不可少的。這就會不可避免地帶來舍入誤差,故需要有一種方法來衡量結(jié)果與采用無窮精度計(jì)算時(shí)的差距。我們來觀察一下 b=10 和 p=4 的浮點(diǎn)格式。用這種格式,.0123456 可以表達(dá)成 1.234x10^(-2)。很明顯這種表達(dá)方式在最后位置單位 (ulps) 發(fā)生了 .56 的差異。又如,如果浮點(diǎn)計(jì)算的結(jié)果是 4.567x10^(-2),而采用無窮精度計(jì)算的結(jié)果是 4.567895,則最后差 .895 最后位置單位。
“最后位置單位”(ulps) 是規(guī)定這種計(jì)算誤差的一種方法。相對誤差是另一種用來衡量浮點(diǎn)數(shù)近似實(shí)數(shù)誤差的方法。相對誤差被定義為實(shí)數(shù)與浮點(diǎn)數(shù)之差除以實(shí)數(shù)的商。比如,將 4.567895 用浮點(diǎn)數(shù)表達(dá)為 4.567x10^(-2) 時(shí)的相對誤差為 .000895/4.567895≈.00019。根據(jù)標(biāo)準(zhǔn)的要求,每個(gè)浮點(diǎn)數(shù)的正確計(jì)算結(jié)果的誤差應(yīng)不大于 0.5ulps。
浮點(diǎn)系統(tǒng)設(shè)計(jì)
在為數(shù)值應(yīng)用開發(fā)設(shè)計(jì)的時(shí)候,很重要的一環(huán)是考慮輸入的數(shù)據(jù)或者常數(shù)是否可以為實(shí)數(shù)。如果可以為實(shí)數(shù),則在完成設(shè)計(jì)之前需要注意一些問題。需要檢驗(yàn)來自數(shù)據(jù)集的數(shù)值在浮點(diǎn)表達(dá)式中是否很接近,有沒有出現(xiàn)過大或者過小的情況。下面以二次方程求根為例。二次方程的根α 和 β 分別表達(dá)為下面兩個(gè)等式:
很明顯,如果 b^2 和 4ac 是浮點(diǎn)數(shù),就會遇到舍入誤差問題。如果 b^2 遠(yuǎn)大于 4ac,則 √(b^2 ) =|b|。如果b>0,β 將有解,但 α 的解將為 0,因?yàn)槠浞肿又械母黜?xiàng)相互抵消。另一方面,如果 b < 0,則 β 的解為 0,α 將有解。這是一種錯(cuò)誤的設(shè)計(jì),因?yàn)樵诟↑c(diǎn)計(jì)算的作用下,根的值為 0,如果用于后續(xù)的設(shè)計(jì),將很容易導(dǎo)致錯(cuò)誤的輸出。
防止這種錯(cuò)誤抵消的方法之一是將 α 和 β 的分子和分母同時(shí)乘以 -b-√(b^2-4ac),這樣得到:
現(xiàn)在,在 b>0 的情況下,應(yīng)使用 α’ 和 β 求出兩個(gè)根的值。在 b<0 的情況下,應(yīng)使用 α 和 β’ 求出兩個(gè)根的值。在設(shè)計(jì)中很難察覺到這樣的誤差,原因在于對于整數(shù)數(shù)據(jù)集來說,常規(guī)公式會得出正確的結(jié)果,但在使用浮點(diǎn)數(shù)據(jù)的時(shí)候,常規(guī)公式會讓設(shè)計(jì)返回錯(cuò)誤的結(jié)果。
另一個(gè)有趣的實(shí)例是計(jì)算三角形面積。根據(jù) Heron 公式,在已知三角形邊長的情況下,三角形的面積 A 可以用下面的公式求出:
這里 a,b 和 c 是三角形的邊長,s= (a+b+c)/2。
對一個(gè)一邊的長度約等于另外兩邊長度之和的平三角形而言,有 a≈b+c,隨即有 s≈a。由于兩個(gè)相鄰的數(shù)值會在 (s-a) 中相減,所以 (s-a) 可能會等于 0?;蛘?,如果 s或者 a 出現(xiàn)舍入誤差,就會導(dǎo)致顯著的 ulps 誤差。因此,最好根據(jù)數(shù)學(xué)家和計(jì)算機(jī)科學(xué)家 William Kahan (2)在 1986 年率先提出的建議,將此公式改寫為下列格式:
我們看看分別用 Heron 的公式和 Kahan 的公式對相同的數(shù)值進(jìn)行計(jì)算的結(jié)果如何。假定 a=11.0,b=9.0,c=2.04,則正確的 s 值為 11.02,正確的 A 值為1.9994918。但是,在采用 Heron 公式的情況下,計(jì)算得出的 s 值為 11.05,只有 3ulps 的誤差,A 的值為3.1945189,出現(xiàn)巨大的 ulps 誤差。在采用 Kahan 的公式的情況下,得出的 A 值為 1.9994918,小數(shù)點(diǎn)后七位完全與正確的結(jié)果吻合。
處理極小和極大的浮點(diǎn)數(shù)
極小的浮點(diǎn)數(shù)在相乘后會得出更小的結(jié)果。對設(shè)計(jì)人員來說,這可能會導(dǎo)致系統(tǒng)的下溢問題,最終的結(jié)果可能出錯(cuò)。這個(gè)問題在概率處理領(lǐng)域中尤為突出,比如計(jì)算語言學(xué)和機(jī)器學(xué)習(xí)。任何事件的概率一般都在 0 和 1 之間。能夠得到完美整數(shù)結(jié)果的公式在處理非常小的概率時(shí)往往會導(dǎo)致下溢問題。避免下溢的方法之一是采用指數(shù)和對數(shù)。
在對很小的浮點(diǎn)數(shù)做相乘運(yùn)算時(shí),采用下面的公式非常有效:
這里指數(shù)和對數(shù)的底數(shù)必須相同。不過需要注意的是,如果a 和 b 都是概率,值都在 0和 1 之間,那么它們的對數(shù)會在-∞ 和 0 之間。如果只希望處理正概率,則將概率的對數(shù)乘以 -1,就可以正確地解讀結(jié)果。采用對數(shù)值的另一項(xiàng)優(yōu)勢在于速度,因?yàn)橄嗉颖认喑说牟僮魉俣雀臁?/p>
工程人員往往會遇到需要對非常大的連續(xù)數(shù)進(jìn)行相乘的情況,比如:
K 的值在某些情況下非常容易溢出。處理這種情況的方法之一是用下式計(jì)算 k:
當(dāng)然,這樣計(jì)算相對較為復(fù)雜一點(diǎn),但如果想要保證設(shè)計(jì)在計(jì)算這種極端狀況時(shí)不會出錯(cuò),這點(diǎn)代價(jià)還是值得的。
一些常見的浮點(diǎn)微妙之處
浮點(diǎn)算術(shù)并不嚴(yán)格遵循整數(shù)的數(shù)學(xué)規(guī)律。加法和乘法結(jié)合律對浮點(diǎn)數(shù)無效,減法和除法也是如此。造成這種情況的原因有兩個(gè)。首先在順序變動時(shí)會出現(xiàn)下溢或者上溢,其次是舍入誤差問題。請看下面關(guān)于浮點(diǎn)數(shù) a,b 和 c的幾個(gè)例子。
右邊的表達(dá)式在 a 和 b 相乘時(shí)可能出現(xiàn)上溢或者下溢。另外,由于 a 和 b 的乘積先計(jì)算,它的舍入誤差與左邊 b 和c 乘積的舍入誤差不同,結(jié)果導(dǎo)致兩個(gè)表達(dá)式得出的結(jié)果不同。
左邊的 (b-c) 表達(dá)式中,如果兩個(gè)數(shù)的浮點(diǎn)表示足夠接近,會使該項(xiàng)結(jié)果為 0。在這種情況下 a=c。(b-c) 的舍入誤差會導(dǎo)致結(jié)果與 b 不同。
由于舍入誤差,表達(dá)式 (1.0+c) 的結(jié)果會導(dǎo)致值發(fā)生改變,最終與第一個(gè)表達(dá)式的結(jié)果不同。
同理,c/10.0 的舍入誤差與 cx0.1 的不同,導(dǎo)致 b 的值不同。另外,數(shù)值 10.0 可以用 2 為底數(shù)表達(dá)為確切的表達(dá)式,但數(shù)值 0.1 用 2 為底數(shù)不能表達(dá)為確切的表達(dá)式。數(shù)值0.1 在表達(dá)為二進(jìn)制表達(dá)式時(shí),會出現(xiàn) 0011 的無窮循環(huán)。這種不能用特定的底數(shù)進(jìn)行有限表達(dá)的數(shù)被稱為該底數(shù)的非確定數(shù),而能夠進(jìn)行有限表達(dá)的,則被稱為確定數(shù)。
假定 c 可以一直被表達(dá)為確定數(shù),如果 c 在一種情況下與確定數(shù)相除,在另一種情況下與非確定數(shù)相乘,則得到的結(jié)果是不一樣的。而且由于 c 本身也可以是確定數(shù)或不確定數(shù),取決于之前的計(jì)算方法,所以兩個(gè)表達(dá)式之間的等價(jià)性無法成立。
這個(gè)等式在 x 為 0、無窮或者非數(shù)值的情況下是不成立的。在執(zhí)行無效數(shù)學(xué)運(yùn)算的情況下會出現(xiàn) NaN,比如開負(fù)數(shù)的平方根或者用 0 除以 0。在出現(xiàn) NaN 的情況下,程序員負(fù)責(zé)決定下一步怎么做。應(yīng)注意根據(jù) IEEE 754-2008 的規(guī)定,NaN 可以用指數(shù) emax+1 和非零有效數(shù)表達(dá)為浮點(diǎn)數(shù)。因?yàn)橛行?shù)中可以放入由系統(tǒng)決定的信息,故結(jié)果將是一系列NaN 而非唯一的 NaN。因此在處理浮點(diǎn)代碼時(shí),請勿用 1.0替換 x/x,除非能夠確認(rèn) x 不是零、無窮或者 NaN。
在 x 為無窮或者 NaN 的情況下該等式不成立。因此在不能確定 x 取值的情況下請勿用 0.0 替換 x-x。工程人員往往在編譯器中采用這種替換技巧作為優(yōu)化策略。但是正如所看到的,這樣的優(yōu)化可能導(dǎo)致錯(cuò)誤的結(jié)果。
上面的等式在 x 為 -0 的時(shí)候不成立(注意可以有 +0 和-0)。這是因?yàn)楦鶕?jù) IEEE 754-2008 規(guī)定的默認(rèn)舍入規(guī)則,(-0) + (+0) =+0,經(jīng)舍入后為偶數(shù)。
IEEE 標(biāo)準(zhǔn)定義 +0 和 -0 的目的是為了得出答案的符號。比如 4 x (+0) =+0,(+0)/(-4)=-0。為比較目的,可以令+0=-0,這樣像 if (x=0) 這樣的聲明就不用關(guān)心 +0 和 -0 之間的差異了。
更深入的理解
除了 Kahan 的著作,想要更加深入地領(lǐng)會使用浮點(diǎn)數(shù)據(jù)的各種微妙之處,工程人員還可以閱讀 Goldberg 博士的《What Every Computer Scientist Should Know AboutFloating-Point Arithmetic》A 版(加利福尼亞州 Mountain View,1992 年 6 月)。另外有大量優(yōu)秀參考文件對 IEEE 754-2008 標(biāo)準(zhǔn)以及其他影響浮點(diǎn)設(shè)計(jì)的標(biāo)準(zhǔn)(比如 ISO/ IEC 9899:201x)進(jìn)行了詳細(xì)解讀。(3)
像航空電子和安全嵌入式系統(tǒng)這樣的電子行業(yè)領(lǐng)域要求開發(fā)高度精確且無誤的硬件和軟件。由于部分硬件需要處理大量的浮點(diǎn)數(shù)據(jù),因此設(shè)計(jì)人員有必要掌握浮點(diǎn)算術(shù)與整數(shù)算術(shù)之間的差異。
評論