Verilog 中對(duì)浮點(diǎn)數(shù)如何處理
掃描二維碼
隨時(shí)隨地手機(jī)看文章
算法中常常會(huì)到浮點(diǎn)數(shù)運(yùn)算,而浮點(diǎn)數(shù)的處理常常是Verilog初學(xué)中常常遇到的問(wèn)題。以下將就一個(gè)簡(jiǎn)單的例子說(shuō)明Verilog中浮點(diǎn)數(shù)運(yùn)算處理。
在JPEG圖像壓縮時(shí)遇到色彩空間變換的問(wèn)題,將YCbCr轉(zhuǎn)換到RGB會(huì)遇到浮點(diǎn)數(shù)的運(yùn)算,這個(gè)實(shí)現(xiàn)復(fù)雜,以攝氏溫度轉(zhuǎn)換為華氏溫度為例 :F = C x 1.8 + 32
R = 1.164(Y-16) + 1.596(Cr-128)
G = 1.164(Y-16) - 0.391(Cb-128) - 0.813(Cr-128)
B = 1.164(Y-16) + 2.018(Cb-128)
module C2F( iclk,irstn,ic,of); input iclk; input irstn; :0] ic; :0] of; :0] c; :0] of; iclk or negedge irstn) begin begin c <= 0; of <= 0; end else begin c <= ic; of <= c * 1.8 + 32; // 直接處理,在ISE中綜合時(shí)會(huì)報(bào)出錯(cuò) end //ERROR:Xst:850 - "C2F.v" line 31: Unsupported real constant. endendmodule
上面直接用浮點(diǎn)數(shù)進(jìn)行乘法運(yùn)算顯然是會(huì)出錯(cuò)的?。?
//以下為改正后的程序
module C2F( iclk,irstn,ic,of); input iclk; input irstn; :0] ic; :0] of; :0] c; :0] of; :0] sum; iclk or negedge irstn) begin begin <= 0; of <= 0; sum <= 0; end else begin c <= ic; sum <= ic * 7+ 128; of <= (sum >>2); //實(shí)際是 近似計(jì)算:of=(ic*7+128)/4=ic*1.75+32, end endendmodule
將浮點(diǎn)數(shù)乘以相同的擴(kuò)展倍數(shù),然后再將所得結(jié)果除以擴(kuò)展位,這樣可以達(dá)到近似精確的效果!!
謂定點(diǎn)小數(shù),就是小數(shù)點(diǎn)的位置是固定的。我們是要用整數(shù)來(lái)表示定點(diǎn)小數(shù),由于小數(shù)點(diǎn)的位置是固定的,所以就沒(méi)有必要儲(chǔ)存它(如果儲(chǔ)存了小數(shù)點(diǎn)的位置,那就是浮點(diǎn)數(shù)了)。既然沒(méi)有儲(chǔ)存小數(shù)點(diǎn)的位置,那么計(jì)算機(jī)當(dāng)然就不知道小數(shù)點(diǎn)的位置,所以這個(gè)小數(shù)點(diǎn)的位置是我們寫(xiě)程序的人自己需要牢記的!
先以10進(jìn)制為例:如果我們能夠計(jì)算12+34=46的話,當(dāng)然也就能夠計(jì)算1.2+3.4 或者 0.12+0.34了。所以定點(diǎn)小數(shù)的加減法和整數(shù)的相同,并且和小數(shù)點(diǎn)的位置無(wú)關(guān)。乘法就不同了。12*34=408,而1.2*3.4=4.08。這里1.2的小數(shù)點(diǎn)在第1位之前,而4.08的小數(shù)點(diǎn)在第2位之前,小數(shù)點(diǎn)發(fā)生了移動(dòng)。所以在做乘法的時(shí)候,需要對(duì)小數(shù)點(diǎn)的位置進(jìn)行調(diào)整?!可是既然我們是做定點(diǎn)小數(shù)運(yùn)算,那就說(shuō)小數(shù)點(diǎn)的位置不能動(dòng)?。≡趺唇鉀Q這個(gè)矛盾呢,那就是舍棄最低位。 也就說(shuō)1.2*3.4=4.1,這樣我們就得到正確的定點(diǎn)運(yùn)算的結(jié)果了。所以在做定點(diǎn)小數(shù)運(yùn)算的時(shí)候不僅需要牢記小數(shù)點(diǎn)的位置,還需要記住表達(dá)定點(diǎn)小數(shù)的有效位數(shù)。上面這個(gè)例子中,有效位數(shù)為2,小數(shù)點(diǎn)之后有一位。
現(xiàn)在進(jìn)入二進(jìn)制:我們的定點(diǎn)小數(shù)用16位二進(jìn)制表達(dá),最高位是符號(hào)位,那么有效位就是15位。小數(shù)點(diǎn)之后可以有0 - 15位。我們把小數(shù)點(diǎn)之后有n位叫做Qn,例如小數(shù)點(diǎn)之后有12位叫做Q12格式的定點(diǎn)小數(shù),而Q0就是我們所說(shuō)的整數(shù)。
Q12的正數(shù)的最大值是 0 111 . 111111111111,第一個(gè)0是符號(hào)位,后面的數(shù)都是1,那么這個(gè)數(shù)是十進(jìn)制的多少呢,很好運(yùn)算,就是 0x7fff / 2^12 = 7.999755859375。對(duì)于Qn格式的定點(diǎn)小數(shù)的表達(dá)的數(shù)值就它的整數(shù)值除以2^n。在計(jì)算機(jī)中還是以整數(shù)來(lái)運(yùn)算,我們把它想象成實(shí)際所表達(dá)的值的時(shí)候,進(jìn)行這個(gè)運(yùn)算
反過(guò)來(lái)把一個(gè)實(shí)際所要表達(dá)的值x轉(zhuǎn)換Qn型的定點(diǎn)小數(shù)的時(shí)候,就是x*2^n了。例如 0.2的Q12型定點(diǎn)小數(shù)為:0.2*2^12 = 819.2,由于這個(gè)數(shù)要用整數(shù)儲(chǔ)存, 所以是819 即 0x0333。因?yàn)樯釛壛诵?shù)部分,所以0x0333不是精確的0.2,實(shí)際上它是819/2^12 =0.199951171875
我們用數(shù)學(xué)表達(dá)式做一下總結(jié):
x表示實(shí)際的數(shù)(一個(gè)浮點(diǎn)數(shù)), q表示它的Qn型定點(diǎn)小數(shù)(一個(gè)整數(shù))。
q = (int) (x * 2^n)
x = (float)q/2^n
由于/ 2^n和* 2^n可以簡(jiǎn)單的用移位來(lái)計(jì)算,所以定點(diǎn)小數(shù)的運(yùn)算比浮點(diǎn)小數(shù)要快得多。下面我們用一個(gè)例子來(lái)驗(yàn)證一下上面的公式:
用Q12來(lái)計(jì)算2.1 * 2.2,先把2.1 2.2轉(zhuǎn)換為Q12定點(diǎn)小數(shù):
2.1 * 2^12 = 8601.6 = 8602
2.2 * 2^12 = 9011.2 = 9011
(8602 * 9011) >> 12 = 18923
18923的實(shí)際值是18923/2^12 = 4.619873046875 和實(shí)際的結(jié)果 4.62相差0.000126953125,對(duì)于一般的計(jì)算已經(jīng)足夠精確了;
FPGA小數(shù)乘法
計(jì)算內(nèi)容
5.555*4.444=24.68642
第一步:將被乘數(shù)乘以256
5.555*256 = 1422.08 = 20’d1422 = 20’h5_8E; (存在誤差0.0056%)
4.444*256 = 1137.664 = 20’d1137= 20’h4_71; (存在誤差0.058%)
第二步:中間運(yùn)算
20’h5_8E * 20’h4_71 = 20’h18_ABAE;
第三步:中間結(jié)果除以256
20’h18_ABAE >> 8 = 20’h18_AB;
第四步:轉(zhuǎn)換為實(shí)際小數(shù)比較
20’h18_AB = 24.171(存在誤差2%)
注:1、中間乘法操作時(shí),不存在誤差。
2、如果想降低取整導(dǎo)致的誤差,可以加大位寬
加減法運(yùn)算
如果兩個(gè)小數(shù)點(diǎn)的位置不相同,比如說(shuō)分別為0△0101、00△110,代表的十進(jìn)制數(shù)分別是0.3125和0.75。兩個(gè)數(shù)不經(jīng)過(guò)處理,直接相加,Verilog HDL的編譯器按照二進(jìn)制規(guī)則逐位相加,結(jié)果為01011。如果小數(shù)點(diǎn)位置與第一個(gè)數(shù)相同,則表示0.6875。如果小數(shù)點(diǎn)位置與第二個(gè)數(shù)相同,則表示1.375,顯示結(jié)果是不正確的。為了進(jìn)行正確的運(yùn)算,需要在第二個(gè)末位補(bǔ)0,為00△1100,兩個(gè)數(shù)再直接相加,得到“01△1001”,轉(zhuǎn)換成十進(jìn)制數(shù)為1.0625,得到正確的結(jié)果;
綜上所述,如果對(duì)于未對(duì)齊的二進(jìn)制數(shù),需要補(bǔ)齊最低位使得小數(shù)位的位寬相同才能進(jìn)行加減法運(yùn)算;如果將數(shù)據(jù)均看成無(wú)符號(hào)整數(shù),則不需要進(jìn)行小數(shù)位擴(kuò)展,因?yàn)閂erilog HDL編譯器會(huì)自動(dòng)將參與運(yùn)算的數(shù)據(jù)以最低位對(duì)齊進(jìn)行運(yùn)算;
module CP_3_1_alt_calculate_add(input [3:0] d1,input [3:0] d2,output [3:0] unsigned_out, //無(wú)符號(hào)加法輸出output signed [3:0] signed_out //有符號(hào)加法輸出 ); //無(wú)符號(hào)加法運(yùn)算assign unsigned_out=d1+d2; //有符號(hào)加法運(yùn)算wire signed [3:0] s_d1;wire signed [3:0] s_d2;assign s_d1=d1;assign s_d2=d2;assign signed_out=s_d1+s_d2; endmodule
乍眼一看,我們的signed_out及unsigned_out的輸出結(jié)果完全相同,相同的輸入數(shù)據(jù),進(jìn)行無(wú)符號(hào)數(shù)運(yùn)算和有符號(hào)數(shù)運(yùn)算的結(jié)果竟然沒(méi)有任何區(qū)別!對(duì)于加減法來(lái)說(shuō),無(wú)論是否為符號(hào)數(shù)運(yùn)算,其結(jié)果均完全相同,因?yàn)槎M(jìn)制的運(yùn)算規(guī)則相同,如果將二進(jìn)制數(shù)據(jù)轉(zhuǎn)換成十進(jìn)制數(shù)據(jù),我們就可以看出兩者的差別了:
浮點(diǎn)數(shù)的表示方法
一個(gè)浮點(diǎn)數(shù)A由兩個(gè)數(shù)m和e來(lái)表示,即A=m×b^e. 在任意一個(gè)這樣的系統(tǒng)中,我們選擇一個(gè)基數(shù)b(計(jì)數(shù)系統(tǒng)的基)和精度B(使用多少位來(lái)存儲(chǔ))。m(即尾數(shù))是B位二進(jìn)制數(shù),如±d.ddd…ddd。(其中第一個(gè)d必然是1,除非你要表示特別小的數(shù)才是0)
大部分計(jì)算機(jī)采用了二進(jìn)制(b=2)的表示方法,位是衡量浮點(diǎn)數(shù)所需存儲(chǔ)空間的單位,通常為32位或64位,分別叫做單精度和雙精度。單精度擴(kuò)展(>=43位,不常用)、雙精度擴(kuò)展(>=79位,通常采用80位進(jìn)行實(shí)現(xiàn)),實(shí)際上,現(xiàn)在很多計(jì)算機(jī)都遵循這個(gè)標(biāo)準(zhǔn);
符號(hào)位占1bit,指數(shù)位E(Exponent)占8bit,其取值范圍為0~255(無(wú)符號(hào)整數(shù))(注意:E為無(wú)符號(hào)整數(shù),實(shí)際數(shù)值e=E-127,這一點(diǎn)務(wù)必注意),尾數(shù)位M占23bit。尾數(shù)也叫做有效數(shù)字位、系數(shù)位、甚至被稱作小數(shù);
浮點(diǎn)數(shù)的定點(diǎn)化
轉(zhuǎn)成定點(diǎn)數(shù)要定義小數(shù)需求多少位,整數(shù)需求多少位
例:16位的定點(diǎn)數(shù)(MAX:16’d32767 MIN:-32768)
3位整數(shù)位寬,12位的小數(shù)位,最高位的符號(hào)位
取低15位,其中第14,13,12位最大能表示7,
小數(shù)最大12位能表示的最大精度:1/4096=0.000244140625
(0.000244140625*4095 = 0.999755859375)
極限最大值表示:7.999755859375





