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