熊幫玲,吳海燕,程洋洋
(安徽三聯(lián)學(xué)院 電子電氣工程學(xué)院,安徽 合肥 230601)
計(jì)算機(jī)中的所有信息,包括圖片、視頻、音頻、文字、數(shù)據(jù),全部都以二進(jìn)制的形式存在[1]。把現(xiàn)實(shí)中具體的事物通過(guò)建模、抽象等方法用計(jì)算機(jī)語(yǔ)言表示出來(lái),然后由CPU 進(jìn)行處理運(yùn)算,并把結(jié)果以一種人類語(yǔ)言反饋給使用者。這個(gè)過(guò)程中,二進(jìn)制發(fā)揮著難以替代的作用。而日常生活中習(xí)慣使用十進(jìn)制,因此掌握二進(jìn)制與十進(jìn)制的轉(zhuǎn)換就顯得比較重要[2]。
針對(duì)數(shù)制的轉(zhuǎn)換,整數(shù)形式的支持目前已比較完善,資料也很豐富,如二進(jìn)制和十進(jìn)制的相互轉(zhuǎn)換[3]。利用計(jì)算器也可以方便地實(shí)現(xiàn)各種進(jìn)制之間的轉(zhuǎn)換。但當(dāng)前情況下,對(duì)內(nèi)存中浮點(diǎn)數(shù)二進(jìn)制的轉(zhuǎn)換支持并不完善。基于SIMD 的向量浮點(diǎn)單元,可以極大提高運(yùn)算能力,但同時(shí)也增大了驗(yàn)證的難度。特別是在CPU 硅后驗(yàn)證階段,硬件不可靠的情況下,調(diào)試時(shí)就需要把內(nèi)存中浮點(diǎn)數(shù)取出然后轉(zhuǎn)換為十進(jìn)制數(shù)去分析是否符合預(yù)期。例如在龍芯2K1000[4]平臺(tái)上進(jìn)行向量?jī)?yōu)化時(shí)就遇到大量的浮點(diǎn)異常,這與傳遞到浮點(diǎn)寄存器中的數(shù)據(jù)大小有關(guān)。關(guān)于浮點(diǎn)數(shù)二進(jìn)制轉(zhuǎn)換為十進(jìn)制,目前的多數(shù)研究還停留在根據(jù)IEEE754 標(biāo)準(zhǔn)去解析計(jì)算的階段,這個(gè)計(jì)算過(guò)程是比較復(fù)雜且效率低下的。肖紅德[5]對(duì)IEEE754 標(biāo)準(zhǔn)進(jìn)行詳細(xì)的研究分析和計(jì)算,張愛(ài)良[6]在標(biāo)準(zhǔn)基礎(chǔ)上做出一些優(yōu)化改進(jìn)。但這些研究都離不開(kāi)標(biāo)準(zhǔn)最終仍舊需要繁瑣的計(jì)算過(guò)程,費(fèi)時(shí)費(fèi)力,計(jì)算過(guò)程中容易導(dǎo)致錯(cuò)誤,實(shí)際應(yīng)用不好普及。
針對(duì)以上問(wèn)題,本文另辟蹊徑,拋卻直接或間接利用IEEE754標(biāo)準(zhǔn)去計(jì)算二進(jìn)制浮點(diǎn)數(shù)對(duì)應(yīng)的十進(jìn)制數(shù)據(jù)的傳統(tǒng)方法。根據(jù)共用體成員變量共用內(nèi)存地址空間的特性,設(shè)計(jì)一個(gè)共用體,并提供一種算法步驟能夠把浮點(diǎn)數(shù)的二進(jìn)制數(shù)據(jù)轉(zhuǎn)換為十進(jìn)制。實(shí)驗(yàn)表明,本文提出的方法能簡(jiǎn)單高效地完成浮點(diǎn)二進(jìn)制數(shù)據(jù)向十進(jìn)制轉(zhuǎn)換的問(wèn)題[7]。
浮點(diǎn)處理的是數(shù)的近似表示,通常浮點(diǎn)數(shù)的表示都遵守IEEE754 標(biāo)準(zhǔn)[Institute of Electrical and Electronics Engineers 754]。該標(biāo)準(zhǔn)精確定義了一類基本操作應(yīng)當(dāng)產(chǎn)生的結(jié)果,保證程序員不管用什么機(jī)器,只要是同樣的輸入就能獲得同樣的結(jié)果。當(dāng)數(shù)據(jù)在整數(shù)和浮點(diǎn)寄存器之間傳送時(shí),不做任何數(shù)據(jù)轉(zhuǎn)換,也不會(huì)發(fā)生任何異常[8]。IEEE754 標(biāo)準(zhǔn)在編程中并不需要多關(guān)注,因?yàn)槿绻诖a中顯示給予一個(gè)浮點(diǎn)常數(shù),存儲(chǔ)時(shí)會(huì)由計(jì)算機(jī)自動(dòng)按照IEEE754 標(biāo)準(zhǔn)格式存儲(chǔ)。
在32 位機(jī)器上,根據(jù)IEEE754 標(biāo)準(zhǔn),單精度浮點(diǎn)數(shù)(32 位)中每個(gè)bit 定義如下:
IEEE754 標(biāo)準(zhǔn)規(guī)定,單精度浮點(diǎn)數(shù)據(jù)的存儲(chǔ)是按指數(shù)和尾數(shù)的二進(jìn)制形式存儲(chǔ)的,用二進(jìn)制的科學(xué)計(jì)數(shù)法來(lái)表示。把浮點(diǎn)數(shù)對(duì)應(yīng)的內(nèi)存劃分為指數(shù)域、尾數(shù)域和符號(hào)位。指數(shù)域沒(méi)有采用有符號(hào)的二進(jìn)制數(shù)形式存儲(chǔ),而是采用偏置方式(指數(shù)+127,對(duì)于32 位的IEEE 格式,指數(shù)域?yàn)? 位的長(zhǎng)度,可以容納從0 到255 的值),保證指數(shù)域永遠(yuǎn)為正,這樣表示的范圍就大一點(diǎn);尾數(shù)介于1 和2 之間,由于尾數(shù)的最高有效位永遠(yuǎn)為1,因此就沒(méi)必要存儲(chǔ),所以尾數(shù)域存儲(chǔ)的值是從次高位開(kāi)始存儲(chǔ)的,這樣就可以免費(fèi)獲得一個(gè)額外精度位的技巧。這種規(guī)格化形式對(duì)于計(jì)算機(jī)表示很有用,因?yàn)椴恍枰~外的信息記錄小數(shù)點(diǎn)的位置。圖1 所示為浮點(diǎn)數(shù)8.25 在內(nèi)存中的表示。
圖1 浮點(diǎn)數(shù)8.25 的內(nèi)存表示
在計(jì)算機(jī)內(nèi)存系統(tǒng)中,整數(shù)是按照固定的算法轉(zhuǎn)化為二進(jìn)制數(shù)進(jìn)行存儲(chǔ)的,這組二進(jìn)制數(shù)每個(gè)bit都有固定的權(quán)值。二進(jìn)制數(shù)轉(zhuǎn)換成十進(jìn)制數(shù)的基本做法是,把二進(jìn)制數(shù)首先寫(xiě)成加權(quán)系數(shù)展開(kāi)式,然后按十進(jìn)制加法規(guī)則求和。比如對(duì)于一個(gè)8bit 的二進(jìn)制數(shù)據(jù):
轉(zhuǎn)化為十進(jìn)制數(shù)需要按公式(2)所示算法進(jìn)行計(jì)算:
目前,為了獲取浮點(diǎn)數(shù)的十進(jìn)制數(shù)據(jù),需要技術(shù)人員根據(jù)IEEE754標(biāo)準(zhǔn)規(guī)定的浮點(diǎn)數(shù)的二進(jìn)制科學(xué)計(jì)數(shù)法表示格式,解析計(jì)算得到浮點(diǎn)數(shù)的十進(jìn)制數(shù)據(jù)。例如,對(duì)于單精度浮點(diǎn)數(shù),按照公式(3)計(jì)算得到浮點(diǎn)數(shù)的十進(jìn)制數(shù)據(jù)。sign 是符號(hào)位,決定對(duì)應(yīng)的十進(jìn)制浮點(diǎn)數(shù)的正負(fù),0 代表正,1 代表負(fù);mant 是浮點(diǎn)數(shù)二進(jìn)制表示的尾數(shù)域,組成浮點(diǎn)二進(jìn)制科學(xué)計(jì)數(shù)法的小數(shù)部分,注意由于二進(jìn)制的科學(xué)計(jì)數(shù)法的第一位都是1,在表示浮點(diǎn)數(shù)時(shí)都會(huì)省略,所以此處需要再加上整數(shù)部分的1,二者拼接成1.xxxx 的形式;bexp 是指數(shù)域的值,需要轉(zhuǎn)換為十進(jìn)制數(shù)然后減去127;v是轉(zhuǎn)換后的十進(jìn)制數(shù)據(jù)。轉(zhuǎn)換為十進(jìn)制需要在二進(jìn)制科學(xué)計(jì)數(shù)的基礎(chǔ)上分別把二進(jìn)制浮點(diǎn)數(shù)的整數(shù)部分和小數(shù)部分分別轉(zhuǎn)換為十進(jìn)制[9]。
這種獲取浮點(diǎn)數(shù)十進(jìn)制數(shù)據(jù)的方法,需要由技術(shù)人員手動(dòng)或者通過(guò)編程,先從浮點(diǎn)數(shù)的二進(jìn)制數(shù)據(jù)中分別提取符號(hào)位、指數(shù)域和尾數(shù)域的值,然后完成上述較復(fù)雜的計(jì)算過(guò)程,非常容易出錯(cuò),耗時(shí)很長(zhǎng),效率很低。
由上文介紹的浮點(diǎn)數(shù)二進(jìn)制轉(zhuǎn)換為十進(jìn)制的通用做法可以看出傳統(tǒng)轉(zhuǎn)換方式存在的弊端,不易在實(shí)際中使用[10]。因此,尋找新的轉(zhuǎn)換方法迫在眉睫。事實(shí)上,可以在調(diào)試過(guò)程中使用一些技巧,輕松地獲取內(nèi)存中的二進(jìn)制浮點(diǎn)數(shù)據(jù)。針對(duì)浮點(diǎn)數(shù)特殊的存儲(chǔ)形式,通過(guò)將得到的浮點(diǎn)數(shù)二進(jìn)制數(shù)據(jù)賦值給共用體變量的整型成員變量,由于共用體變量中的每個(gè)成員變量共用內(nèi)存地址空間,在將該浮點(diǎn)數(shù)的二進(jìn)制數(shù)據(jù)賦值給共用體變量的整型成員變量后,也就是將浮點(diǎn)數(shù)的二進(jìn)制數(shù)據(jù)存儲(chǔ)到了該共用體變量的浮點(diǎn)型成員變量所指向的內(nèi)存地址空間;通過(guò)打印共用體變量的浮點(diǎn)型成員變量,即可得到浮點(diǎn)數(shù)的十進(jìn)制數(shù)據(jù)。該共用體的C++實(shí)現(xiàn)形式如下。
該共用體包含2 個(gè)成員項(xiàng),一個(gè)是float 類型,另一個(gè)是int 類型。選擇int 類型是因?yàn)樵谝话闱闆r下它占據(jù)的內(nèi)存空間同float 類型一樣,具體可以根據(jù)自己使用的CPU 架構(gòu)確定。這樣當(dāng)一個(gè)浮點(diǎn)數(shù)傳遞進(jìn)來(lái)時(shí)不至于損失數(shù)據(jù),同時(shí)也能節(jié)省內(nèi)存空間;其次利用了共用體的成員變量具有共用內(nèi)存地址空間的特性,這樣當(dāng)把4 個(gè)字節(jié)的數(shù)據(jù)傳遞進(jìn)來(lái)時(shí),2 個(gè)成員變量都獲得了賦值。對(duì)于內(nèi)存中同一個(gè)地址上的二進(jìn)制數(shù),當(dāng)做整型還是浮點(diǎn)型是有不同的意義的,雖然其在二進(jìn)制形式上是一模一樣的。也就是說(shuō),4 個(gè)字節(jié)的二進(jìn)制數(shù)據(jù)本身是無(wú)意義的,當(dāng)把它看成int 類型的變量時(shí),就具有int 類型的值;當(dāng)把它看成float 類型的變量時(shí),就按照IEEE754 標(biāo)準(zhǔn)具有float 類型的值。顯然這組二進(jìn)制數(shù)據(jù)對(duì)應(yīng)的2 種類型的十進(jìn)制數(shù)是有天壤之別的。
C++代碼如下。
C++代碼中首先定義一個(gè)共用體變量ff,把要轉(zhuǎn)換的二進(jìn)制浮點(diǎn)數(shù)據(jù)賦值給int 類型的變量,這樣float 成員變量也獲得了這份二進(jìn)制數(shù)據(jù)。再通過(guò)GDB(在調(diào)試時(shí)使用gdb 命令‘p ff.ff’)或printf 函數(shù)打印ff.ff,即可獲得該二進(jìn)制數(shù)據(jù)對(duì)應(yīng)的十進(jìn)制浮點(diǎn)數(shù)大小,以上代碼是以printf 函數(shù)為例進(jìn)行操作的。該方法完整過(guò)程如圖2 所示。
圖2 浮點(diǎn)二進(jìn)制轉(zhuǎn)十進(jìn)制過(guò)程
這個(gè)過(guò)程也可以反過(guò)來(lái),實(shí)現(xiàn)的效果就是能方便地把一個(gè)浮點(diǎn)數(shù)轉(zhuǎn)化為一個(gè)IEEE754標(biāo)準(zhǔn)下的二進(jìn)制數(shù)。在將浮點(diǎn)數(shù)的十進(jìn)制數(shù)據(jù)賦值給共同體變量的浮點(diǎn)型成員變量之后,通過(guò)調(diào)試工具或者printf函數(shù),打印共同體變量的整數(shù)型成員變量,即可打印出浮點(diǎn)數(shù)的二進(jìn)制數(shù)據(jù)。由于高級(jí)語(yǔ)言能夠獨(dú)立于不同處理器的特性,使用編譯器為不同機(jī)器生成不同的目標(biāo)代碼(或機(jī)器指令)。因此,經(jīng)過(guò)編譯器的轉(zhuǎn)換,從高級(jí)語(yǔ)言出發(fā)設(shè)計(jì)的共用體在不同架構(gòu)的微處理器上僅表現(xiàn)為不同的指令碼,本文提出的方法在不同架構(gòu)的微處理器上都可適用。
在龍芯2K1000 平臺(tái)上進(jìn)行skia 圖形庫(kù)浮點(diǎn)向量?jī)?yōu)化時(shí),遇到大量float exception 導(dǎo)致的程序崩潰問(wèn)題[11]。經(jīng)過(guò)仔細(xì)分析代碼流程與反匯編指令序列,確定代碼無(wú)誤的情況下查閱2k1000 GS264 處理器核用戶手冊(cè),最終確定float exception 是向量浮點(diǎn)指令傳入的操作數(shù)小于手冊(cè)上規(guī)定的最小規(guī)格化數(shù)導(dǎo)致的。使用GDB 調(diào)試工具可以獲取到指令與寄存器中的二進(jìn)制數(shù)據(jù),但是對(duì)于這些二進(jìn)制對(duì)應(yīng)的十進(jìn)制數(shù)是否都是小于最小規(guī)格化數(shù)是不好確定的。對(duì)于優(yōu)化過(guò)程中使用的大量浮點(diǎn)指令傳入的操作數(shù),以及程序運(yùn)行時(shí)的每一次加載進(jìn)浮點(diǎn)向量寄存器的二進(jìn)制數(shù)據(jù),顯然是不能通過(guò)傳統(tǒng)方式計(jì)算的。通過(guò)使用本方法,能夠方便快速地求出發(fā)生浮點(diǎn)異常時(shí)寄存器中二進(jìn)制數(shù)據(jù)對(duì)應(yīng)的十進(jìn)制數(shù)據(jù),進(jìn)而和手冊(cè)進(jìn)行比較,最終確定了所有的浮點(diǎn)異常都是這個(gè)原因?qū)е碌?。? 是利用本文提供的方法進(jìn)行二進(jìn)制和十進(jìn)制的數(shù)據(jù)轉(zhuǎn)換結(jié)果。
表1 浮點(diǎn)二進(jìn)制數(shù)轉(zhuǎn)化十進(jìn)制
本文通過(guò)設(shè)計(jì)一個(gè)共用體類型,提出將浮點(diǎn)數(shù)的二進(jìn)制數(shù)據(jù)賦值給共用體變量的整型成員變量,借助GDB 或者printf函數(shù)去獲取對(duì)應(yīng)的十進(jìn)制值的方法,解決了傳統(tǒng)方式浮點(diǎn)二進(jìn)制數(shù)據(jù)向十進(jìn)制數(shù)據(jù)轉(zhuǎn)換過(guò)程復(fù)雜及容易出錯(cuò)的問(wèn)題。在需要大量進(jìn)行這種數(shù)據(jù)轉(zhuǎn)換的場(chǎng)合,本文提出的方法更是有很高的效率。本文僅通過(guò)float 類型的數(shù)據(jù)作為例子介紹浮點(diǎn)二進(jìn)制向十進(jìn)制轉(zhuǎn)換的方法,對(duì)于double 類型,以及浮點(diǎn)向量類型的數(shù)據(jù)轉(zhuǎn)換,也具有拋磚引玉的意義。
遼寧工業(yè)大學(xué)學(xué)報(bào)(自然科學(xué)版)2021年3期