天天看點

《計算機系統:核心概念及軟硬體實作(原書第4版)》——3.5浮點數表示

本節書摘來自華章計算機《計算機系統:核心概念及軟硬體實作(原書第4版)》一書中的第3章,第3.5節,作者:[美] j. 斯坦利·沃法德(j. stanley warford)著, 更多章節内容可以通路雲栖社群“華章計算機”公衆号檢視。

本章前面幾節描述的數值表示是對于整數值的。c++有3種數值類型有小數部分:

float 單精度浮點數

double 雙精度浮點數

long double 長雙精度浮點數

這些類型的值在isa3層不能以補碼的形式存儲,因為存儲必須提供存放數字中小數點位置的方式。浮點數值用科學計數法的二進制版本來存儲。

3.5.1二進制小數

二進制小數有一個二進制小數點,它是十進制小數點的二進制版本。

《計算機系統:核心概念及軟硬體實作(原書第4版)》——3.5浮點數表示

例3.33圖3-25a展示了101.011(bin)的位置值。二進制小數點左邊的位與圖3-2無符号二進制表示中相應的位有相同的位置值。二進制小數點右邊的位置值從1/2開始,每個位置值是前一位的一半。圖3-25b給出的加法表明得到的值是5.375(dec)。 □

圖3-26是有小數部分數字的多項式表示。小數點左邊一位的位置值總是基數的0次方,即1。往左下一個有效位是基數的1次方,即基數本身。小數點右邊一位的位置值是基數的-1次方,往右下一個有效位是基數的-2次方,右邊每個位置值是它左邊位位置值的1/基數倍。

确定二進制小數的十進制值分為兩步。首先,用例3.3中無符号二進制數轉換的方法轉換二進制小數點左邊的位。然後,用逐位翻倍的算法轉換二進制小數點右邊的位。

例3.34圖3-27展示了把6.585 937 5(dec)轉化為二進制的過程。轉換整數部分就是轉化小數點左邊的110(bin);轉換小數部分是把小數點右邊的數字寫在表格右列的頭部,小數部分乘以2,小數點左邊的數字寫在左列,小數部分寫在右列。下次乘2時,不包括整數部分。例如,把.171 875乘2得到0.343 75,而不是把1.171 875乘2。左列從上到下的數字就是二進制小數部分從左到右的位,是以6.585 937 5(dec)= 110.100 101 1(bin)。 □

把小數部分從十進制轉換到二進制的算法就像是把整數部分從十進制轉換到二進制算法的鏡像。圖3-5給出了用逐位除以2的算法轉換十進制整數的過程。除法的餘數就是得到的數字位,順序是從二進制小數點開始從右往左。用逐位乘以2的算法轉換小數部分,乘法得到的整數部分是生成的數字位,順序是從二進制小數點開始從左往右。

《計算機系統:核心概念及軟硬體實作(原書第4版)》——3.5浮點數表示

一個可以用有限位十進制表示的數,它的二進制數表示可能是無限位的。

例3.35圖3-28展示的是把0.2(dec)轉換為二進制的過程。第一次乘2得到0.4,反複幾次後又得到了0.4。顯然這個過程不會終止,是以0.2(dec)=0.001100110011...(bin),位模式0011會不斷重複。   □

《計算機系統:核心概念及軟硬體實作(原書第4版)》——3.5浮點數表示

由于所有計算機單元都隻能存儲有限的位,是以0.2(dec)不能被精确存儲,必定是一個近似值。我們應該意識到由于二進制表示值的固有舍入誤差,是以如果用像c++這樣的hol6層語言做加法0.2 + 0.2,也許不會精确得到0.4。正是由于這個原因,好的數值軟體幾乎不會檢測兩個浮點數是否完全相等,而是用軟體維護了一個很小的非零容忍值,用以表示如果兩個浮點數的差小于該值就被看作相等。如果容忍值是0.0001,那麼1.382 64和1.382 67會被認為是相等的,因為它們的差0.000 03小于容忍值0.0001。

3.5.2餘碼表示

可以用常見于十進制數的科學計數法的二進制版本來表示浮點數。一個以科學計數法表示的非零數是規格化的,如果它的第一個非零位正好在小數點左邊。因為0沒有第一個非零位,是以0不能被規格化。

例3.36十進制數-328.4的科學計數法規格化表示是-3.284×102,10的2次方的作用是把小數點往右移動2位。類似地,二進制數-10101.101的科學計數法規格化形式是-1.0101101×24,2的4次方的作用是把二進制小數點往右移動4位。 □

例3.37二進制數0.00101101的科學計數法規格化表示是1.01101×2-3,2的-3次方的作用是把二進制小數點往左移動3位。 □

《計算機系統:核心概念及軟硬體實作(原書第4版)》——3.5浮點數表示

一般來說,浮點數可以是正數或負數,它的指數也可以是正整數或負整數。圖3-29展示了存儲浮點數值的一個記憶體單元。單元分為3個字段,第一個字段1位,用于存儲該數的符号,第二個字段存儲代表規格化二進制數的指數位,第三個字段稱為有效位數,存儲代表數值大小的位。

任何有符号整數的表示方法都可以用于存儲指數。你可能會想到用補碼表示,因為大多數計算機存儲有符号整數都用它。但是實際上沒有用補碼,而是用了有偏差的表示方法,後面很快會解釋這個原因。

一個5位單元有偏差表示的例子是餘15碼(excess 15)。單元存儲數字的範圍十進制表示為-15到16,二進制表示為00000到11111。把十進制轉換到餘15碼是把十進制數值加15,然後按照無符号數方式轉換為二進制。從餘15碼轉換為十進制是把它按照無符号數寫作十進制數,然後減去15。在餘15碼中,第一位表示一個值是正還是負,不過與補碼表示不一樣,1表示正值,0表示負值。

例3.38把十進制5轉換到餘15碼,5 + 15=20。然後按照無符号數方法把20轉換為二進制,20(dec)=10100(excess 15)。第一位是1表示是一個正值。 □

例3.39把00011從餘15碼轉換到十進制,把00011當作無符号值轉換,00011(bin)= 3(dec),然後3 - 15=-12,是以00011(excess 15)=-12(dec)。 □

《計算機系統:核心概念及軟硬體實作(原書第4版)》——3.5浮點數表示

圖3-30展示了一個3位單元以餘3碼表示和以補碼表示存儲整數的比較。每種表示法存儲8個值,餘3碼的表數範圍是-3到4(dec),而補碼的是-4到3(dec)。 □

3.5.3隐藏位

假設浮點數以第一個非零位直接存儲為小數點左邊的規格化形式,那麼就不需精确地存儲二進制小數點,因為它總是在同樣的位置。假設圖3-29的符号位為1表示負值,為0表示正值,指數是3位,尾數是4位,那麼可以存儲尾數為4位的數字。要存儲十進制值,首先把它轉換為二進制,寫成規格化的科學計數法的形式,然後以餘3碼的形式存儲指數,再存儲尾數的多個最高有效位。

例3.40 存儲0.34,把它轉換到二進制0.34(dec)=0.010101110...,這個位序列是無窮盡的,是以隻存儲最高位。這個數的規格化科學計數法值是1.0101110…×2-2,指數是-2。從圖3-30可以看到它的餘3碼表示是001。最高的4位是1010,它在第一個數字的後面隐含了小數點。這個數是正數,是以符号位是0。存儲這個值的位模式是0 001 1010。

來看一看這個近似值有多接近,把存儲值轉換回十進制。存儲值1.010×2-2(bin)=0.3125,它和原始十進制值相差0.0275。 □

令人遺憾的是不能在尾數中存儲更多的高位數字。當然,與實際機器中的浮點數格式相比,指數3位,尾數4位是太小了。例子采用這麼小的表示是為了說明起來簡單。然而,即使在實際的機器中尾數字段大得多,近似度好很多,但是仍然不可避免近似的發生,因為記憶體單元是有限的。

可以利用當數字用規格化表示時二進制小數點左邊總是1的這個事實。因為1總是在那裡,是以可以簡單地不存儲它,這可以給尾數擴充1位精确度的空間。這個假定在二進制小數點左邊而又不顯式存儲的位叫作隐藏位(hidden bit)。

例3.41采用假定在尾數中有隐藏位的表示方法,0.34(dec)存儲為0 001 0101。二進制小數點右邊的前4位是0101,小數點左邊的1位是假定的。來看看精确度的改進。現在的存儲值是1.0101×2-2(bin)=0.328 125,它與原始的十進制值相差0.011 875,沒有隐藏位的差是0.0275,是以使用隐藏位改進了近似值。 □

當然隐藏位是假定的,不要忽略它。當在一個程式中寫十進制浮點數時,編譯器生成代碼把值轉換為二進制,會丢棄假定存在的隐藏位,盡可能多地存儲二進制小數點右邊的位。如果程式把兩個存儲的浮點數相乘,那麼計算機在執行乘法運算前,會抽取尾數位并插入假設的隐藏位。對于乘積,會除去隐藏位後才進行存儲。

3.5.4特殊值

有些實際值需要特殊看待,最明顯的是0,因為它的二進制表示中沒有為1的位,是以它不能規格化表示,必須為它設定一個特殊的位模式。标準的做法是把指數全置為0,尾數也全置為0。符号位呢?最常見的是0有兩種表示:一個正0,一個負0。如果指數3位,尾數4位,兩種0的位模式是

1 000 0000(bin)=-0.0(dec)

0 000 0000(bin)=+0.0(dec)

不過,0的存儲還有其他解決方案。如果+0.0的位模式沒有特殊指定,那麼0 000 0000會被解讀成有隐藏位,看作1.0000×2-3(bin)=0.125,如果這個值沒有被保留為0,那麼這就是可以存儲的最小正值。如果這個位模式為0保留,那麼可存儲的最小正值是略大的0 000 0001=1.0001×2-3(bin)=0.132 812 5。除了符号位是1,數值最小負數的尾數應該是一樣。具有最小非0尾數的數應該是

1 000 0001(bin)=-0.1328125(dec)

0 000 0001(bin)=+0.1328125(dec)

可以存儲的最大正整數的位模式應該具有最大指數和最大尾數,而具有最大數值大小的負數應該是一樣的位模式,除了符号位為1。具有最大數值大小的位模式和它們的十進制值應該是

1 111 1111(bin)=-31.0(dec)

0 111 1111(bin)=+31.0(dec)

《計算機系統:核心概念及軟硬體實作(原書第4版)》——3.5浮點數表示

圖3-31是0有唯一特殊值的表示方式所對應的數軸。和整數表示一樣,可以存儲多大的值是有限制的。如果9.5乘以12.0,兩者都在範圍内,但是乘積的真實值114.0在正上溢區。

然而,和整數值不一樣的是,實數軸有下溢區。如果0.125乘以0.125,兩者都在範圍内,但乘積的真值0.156 25卻在正下溢區,可以存儲的最小正值是0.132 815。

當計算以确切的精度進行時,近似的浮點數的數值計算結果要和預期保持一緻。例如,假設9.5乘以12.0,結果應該存儲什麼呢?假設把最大值31.0作為近似結果存儲。再假設它是一個更長計算的中間值,然後要計算它的一半是多少,将得到15.5,這和正确的值相差甚遠。

在下溢區有同樣的問題。如果把0.0作為0.156 25的近似值存儲,然後再把它乘以12.0,就會得到0.0。你會有被看上去合理的值誤導的風險。

由上溢和下溢引起的這些問題,通過引入更多的特殊值會有所改善。與0的表示一樣,必須用一些特殊的位模式來表示這些特殊值,如果這些位模式不用來表示特殊值,就可以用來表示數軸上的值。除了0以外,有3個常用的特殊值:

無窮大

非數

非規格化數

無窮大用于表示溢出區的值。如果運算結果溢出,那麼就存儲無窮大的位模式。如果再對這個位模式執行運算,結果就是預期的值—無窮。例如,3/∞=0,5 +∞=∞,而無窮大的平方根是無窮大。除以0會得到無窮大,例如,3/∞=∞,-4/∞=-∞。如果實數做計算得到無窮大,那麼就可以知道某個中間結果發生了溢出。

如果一個值不是一個數(即,非數),它的位模式稱為nan。用nan來表示非法的浮點運算,例如,取負數的平方根得到nan,0/0也得到nan。任何至少有一個nan操作數的浮點運算都得到nan,例如,7 + nan=nan。

無窮大和nan的位模式都使用了指數的最大正值,即指數字段是全1。無窮大的尾數為全1,nan的尾數可以是任何非零模式。把這些位模式保留給無窮大和nan會減少可以存儲的值的範圍。對于3位指數和4位尾數來說,最大數值的位模式和它們的十進制值是

1 111 0000(bin)=-∞

1 110 1111(bin)=-15.5(dec)

0 110 1111(bin)=+15.5(dec)

0 111 0000(bin)=+∞

在圖3-31中,上溢出區的無窮大值,沒有對應的下溢區的無窮小值。不過非規格化數是特殊的值,它們有一個很好的屬性,稱為逐級下溢。有了逐級下溢,最小正值和0之間的差距減小了很多。主要思想是,選取那些指數字段全0的非零值,把它們平均分布在下溢區中。

因為指數字段全0是為非規格化數保留的,是以最小正規格化數是0 001 000=1.000× 2-2(bin)=0.25(dec)。如果指數字段可以是000,那麼最小正規格化數是0.132 812 5,我們現在的做法似乎把事情弄得更糟了。但是,非規格化數分布在原來表示方法的下溢區間内,實際上是減小了下溢區的大小。

當指數字段全是0、尾數至少包含一個1時,要使用特殊的表示規則。假定指數是3位,尾數是4位,

假定二進制小數點左邊的隐藏位為0,而不是1。

假定指數以餘2碼而不是餘3碼的形式存儲。

例3.42對于3位指數、4位尾數的表示方法來說,0 000 0110表示什麼十進制值?因為指數全是0,尾數至少包含一個1,是以這個數是非規格化數,它的指數是000(餘2碼)=0 - 2=-2,它的隐藏位是0,是以它的二進制科學計數值是0.0110×2-2 。因為這是非規格化數的特殊情況,是以指數以餘2碼而不是餘3碼表示。将這個二進制值轉換為十進制,得到0.093 75。 □

這樣的表示會讓下溢區的表示變得更好。計算具有最小數值的數,它是非規格化的。

1 000 0001(bin)=-0.015625(dec)

1 000 0000(bin)=-0.0

0 000 0000(bin)=+0.0

0 000 0001(bin)=-0.015625(dec)

如果沒有非規格化數,最小正數是0.132 812 5,是以實際上下溢區變小了很多。

圖3-32展示了具有所有特殊值的3位指數和4位尾數表示法的一些重要值。這些值按照數字從小到大的順序排列。圖3-32表明為什麼要用餘碼來表示浮點數的指數。忽略符号位,隻考慮從0.0到+∞的正數。可以看到,如果把最右邊的7位看作一個簡單的無符号整數,那麼從表示0的000 0000到表示∞的111 0000,相鄰的值都是加1。如果對兩個浮點數進行比較,比如這樣一條c++語句

《計算機系統:核心概念及軟硬體實作(原書第4版)》——3.5浮點數表示

那麼計算機不需要提取指數字段或者插入隐藏位,隻需要把最右邊7位當作整數,進行比較,就能判斷哪個浮點數有更大的數值。整數運算的電路要比浮點數的快很多,是以用餘碼表示指數實際上提高了性能。

《計算機系統:核心概念及軟硬體實作(原書第4版)》——3.5浮點數表示

對于負數,也有同樣的模式。可以把最右的7位看作無符号整數,用以比較負數的數值大小。如果指數用補碼表示,浮點數就不會有這樣的屬性。

如果x值計算為-0.0,y值計算為+0.0,那麼程式員會預期表達式(x3.5.5ieee 754浮點數标準

電氣電子工程師學會(ieee)是一個由會員支援的專業協會,為各種工程領域提供服務,計算機工程就是其中之一。協會内有各種小組起草工業标準。在ieee提出它的浮點數标準之前,每個計算機廠商都設計它們自己的浮點數值表示法,互不相同。在網絡普及前的早期,計算機之間的資料共享很少,是以這種情況尚可容忍。

即便沒有大量的資料共享需求,标準的缺失也阻礙了數字計算的研究和發展。在兩台不同的計算機上運作兩個一樣的程式,同樣的輸入可能産生不同的結果,原因是兩台計算機采用了不同的近似值表示法。

1985年ieee設立了一個委員會來起草浮點數标準。最終産生了兩個标準:854更适用于手持電腦,而754廣泛應用于計算機。實際上,現在每個計算機廠商的計算機中的浮點數都遵循ieee 754标準。

在本節前面講述的浮點數表示法中,除了指數字段和有效位的數位不同之外,其餘的都和ieee 754是一樣的。圖3-33展示了這個标準的兩種格式:單精度格式的指數字段是8位單元,采用餘127碼表示(除了非規格化數,它們用的餘126碼),尾數是23位;雙精度格式的指數字段是11位單元,采用餘1023碼表示(除了非規格化數,它們用的餘1022碼),尾數是52位。

《計算機系統:核心概念及軟硬體實作(原書第4版)》——3.5浮點數表示

有下列單精度格式的位值,正無窮大是

0 1111 1111 000 0000 0000 0000 0000 0000

寫成4位一組的全32位模式為

0111 1111 1000 0000 0000 0000 0000 0000

它的十六進制簡化表示為7f80 0000(hex)。最大的正值為

0 1111 1110 111 1111 1111 1111 1111 1111

其值近似是2128或1038,它的十六進制表示是7f7f ffff(hex)。最小的規格化正數是

0 0000 0001 000 0000 0000 0000 0000 0000

它的十六進制表示是0080 0000(hex)。最小的非規格化正數是

0 0000 0000 000 0000 0000 0000 0000 0001

它的十六進制表示是0000 0001(hex),近似值為10-45。

william v. kahan

1933年,william v. kahan生于加拿大。他曾就讀于多倫多大學,1958年獲得數學博士學位。

1976年,intel計劃為它的微處理器産品線構造一個浮點協處理器。john palmer負責該項目,他說服intel需要一個數學标準,這樣公司生産的不同晶片對于相同的浮點輸入就能得到相同的輸出。在stanford大學時,palmer聽說過kahan分析了一些當時流行的計算機的浮點值表示。他雇用kahan作為咨詢專家,建立浮點數表示法的細節。

在那之後,ieee成立了一個委員會來開發業界的浮點數标準。kahan是該委員會的成員,雖然剛開始有一些争議,但是他在intel的工作還是成為了ieee 754标準的基礎。當時,digital equipment corporation(dec)在它的vax系列計算機上采用了一種廣受重視的浮點數表示法。在剛開始與palmer接觸時,kahan甚至建議intel就采用該方法。但是vax的表示法沒有逐級下溢的非規格化數。在委員會的讨論中,因為認為這種表示法的所有實作執行起來都很慢,是以這個特性成為一個很大的問題。這場關于逐級下溢的論戰持續多年,dec聲稱具有逐級下溢特性的計算性能都不可能超過vax。最後,加利福尼亞大學伯克利分校的dave patterson的一個研究所學生,george taylor,建構了一個kahan的浮點數标準的工作原型電路闆,可以插入vax機器而不會降低機器的速度。

本章略去了很多有關ieee 756的細節,包括保護數字、異常和标志等方面的規定。kahan緻力于“讓數值計算的世界更安全”。實際上,所有硬體都遵循該标準,隻是有些軟體系統沒有很好地利用異常和标志。當發生這種情況時,kahan就會很快公布這些問題。sun microsystems在推廣java語言時用到這樣一句口号“寫一次,随處運作”,就曾經被kahan在名為“how java抯 floating-point hurts everyone everywhere”的論文中批評過。當matlab軟體最新釋出的版本沒有像較早的版本那樣遵循ieee 754标準時,kahan的論文題目就是“matlab抯 loss is nobody抯 gain”。

1989年,william kahan因為他在數值分析方面的基礎性貢獻獲得了a . m. turing獎。在本書書寫之時,他是加利福尼亞大學伯克利分校數學系和電器工程和計算機科學系的教授。

例3.43-47.25的單精度浮點數的十六進制表示是什麼?整數47(dec)=101111(bin),小數0.25(dec)=0.01(bin),是以

《計算機系統:核心概念及軟硬體實作(原書第4版)》——3.5浮點數表示

。這個數是負數,是以第一位是1,指數5通過5+127=132(dec)=1000 0100(餘127)轉換為餘127碼,尾數存儲二進制小數點右邊的0111101,是以位模式是

1 1000 0100 011 1101 0000 0000 0000 0000

十六進制表示為c23d 0000(hex)。 □

例3.44十六進制表示為3cc8 0000的二進制科學表示法是什麼?它的位模式為0 0111 1001 100 1000 0000 0000 0000 0000,符号位是0,是以這個數是正數,指數是0111 1001(excess 127)=121(unsigned)=121-127 =-6(dec),尾數的小數點右邊是1001,隐藏位為1,是以這個數是

《計算機系統:核心概念及軟硬體實作(原書第4版)》——3.5浮點數表示

。 □

例3.45十六進制表示為0050 0000的二進制科學表示法是什麼?它的位模式是0 0000 0000 101 0000 0000 0000 0000 0000,符号位是0,是以它是正數,指數字段全是0,是以它是非規格化數,指數0000 0000(excess 126)=0(unsigned)=0-126=-126(dec),隐藏位是0而不是1,是以這個數是

《計算機系統:核心概念及軟硬體實作(原書第4版)》——3.5浮點數表示

。 □

繼續閱讀