單精度數

單精度存儲格式

符號位S(sign) - 1bit
0代表正號,1代表負號。(+0、-0視爲相同?(歡迎補充資料)) +0是0000 0000
-0是1111 1111 用-128代替 它沒有反碼補碼
指數位E(exponent) - 8bit
E的取值範圍爲0-255(無符號整數),雙精度爲11位,擴張型大於等於15位,實際數值e=E-127。
有時E也稱爲“移碼”,或稱爲“階碼
尾數位M(mantissa) - 23bit
M也叫有效數字位(significant)、係數位(coefficient),甚至被稱作“小數”。
在一般情況下,m=(1.M)2,使得實際起作用範圍爲1≤尾數<2。
爲了對溢出進行處理,以及擴展對接近0的極小數值的處理能力,IEEE 754對M做了一些額外規定,參見後文介紹。

單精度浮點數實例

對於內部存儲數據(00111111 01100110 01100110 01100110)2:
符號位
(最左側)S=0。這表示是個正數
指數
(左側第2-9位)E=(01111110)2=(126)10,所以e=E-127=-1。
尾數
(最後的23位)M=(1100110 01100110 01100110)2,m=(1.M)2=(1.7999999523162841796875)10
該二進制小數轉爲10進制的計算方式爲1 + (1/2+1/4) + (1/32+1/64) + (1/512+1/1024)……
實際值
N=1.7999999523162841796875*2^-1=0.89999997615814208984375
(其實,這個數據是0.9的單精度浮點數的實際內部存儲,可以看到有一定的誤差)
這裏繼續給出另外幾個數字的實例:
使用豎線|將各個段位分隔顯示
實際值 | 符號位 | ;指數 | ;尾數
1 | 0 | 01111111 | 00000000000000000000000
2 | 0 | 10000000 | 00000000000000000000000
⒍5 | 0 | 10000001 | 10100000000000000000000
-6.5 | 1 | 10000001 | 10100000000000000000000

單精度浮點數的表示範圍及說明

表示範圍

最大表示範圍:單精度浮點數可以表示的範圍爲±3.40282 * 10^38(
單精度

  單精度

1.1111...1×2^127)
接近於0的最小值:單精度浮點數可以表示1.175 * 10-38(1.00...0×2^-126)的數據而不損失精度。
當數值比以上值小的時候,將會由於尾數的有效位數減少而逐步喪失精度(IEEE 754的規定),或者有的系統則直接採用0值來簡化處理過程。
精度
單精度浮點數的實際有效精度爲24位二進制,這相當於 24*log102≈7.2 位10進制的精度,所以平時我們說“單精度浮點數具有7位精度”。(精度的理解:當從1.000...02變化爲1.000...12時,變動範圍爲2^23,考慮到因爲四捨五入而得到的1倍精度提高,所以單精度浮點數可以反映2^24的數值變化,即24位二進制精度)
誤差
浮點數以有限的32bit長度來反映無限的實數集合,因此大多數情況下都是一個近似值。同時,對於浮點數的運算還同時伴有誤差擴散現象
特定精度下看似相等的兩個浮點數可能並不相等,因爲它們的最小有效位數不同。
由於浮點數可能無法精確近似於十進制數,如果使用十進制數,則使用浮點數的數學或比較運算可能不會產生相同的結果。
如果涉及浮點數,值可能不往返。值的往返是指,某個運算將原始浮點數轉換爲另一種格式,而反向運算又將轉換後的格式轉換回浮點數,且最終浮點數與原始浮點數相等。由於一個或多個最低有效位可能在轉換中丟失或更改,往返可能會失敗。

不同實現中的單精度存儲變形

單精度浮點數應用廣泛,而一些低成本的單片機系統中不具備數學運算的協處理器硬件,因而在在不同系統中,根據硬件特性對浮點數的軟件實現進行了相應調整和簡化。
單精度浮點數存儲格式

  單精度浮點數存儲格式

存在如下一些IEEE 754常見變形:
高低位的字節順序不同
即高字節在先的big endian和低字節在先的little endian。後者在Intel、Motorola等的CPU中大量使用。
指數部分被單獨存爲一字節
獨立字節比較便於處理。此類系統會把符號位與尾數結合起來存放。
此外,不同系統中對於下列特性可能有細微差異:
無窮大、NaN的規定和處理
這可能會影響到溢出特性
非歸一化數據的處理
有可能爲了簡單,而將無法歸一化的小數值直接以0來處理;有的則直接採用(0.M)2方案表示全域尾數,以犧牲1位二進制精度的代價換取算法的同意。
以上兩部分的改變,對大多數的應用情形影響不大。

單雙精度浮點數的IEEE標準格式

目前大多數高級語言(包括C)都按照IEEE-754標準來規定浮點數的存儲格式,IEEE754規定,單精度浮點數用4字節存儲,雙精度浮點數用8字節存儲,分爲三個部分:符號位、階和尾數。階即指數,尾數即有效小數位數。單精度格式階佔8位,尾數佔24位,符號位1位,雙精度則爲11爲階,53位尾數和1位符號位。
細心的人會發現,單雙精度各部分所佔字節數量比實際存儲格式都了一位,的確是這樣,事實是,尾數部分包括了一位隱藏位,允許只存儲23位就可以表示24位尾數,默認的1位是規格化浮點數的第一位,當規格化一個浮點數時,總是調整它使其值大於等於1而小於2,亦即個位總是爲1。例如1100B,對其規格化的結果爲1.1乘以2的三次方,但個位1並不存儲在23位尾數部分內,這個1是默認位。
階以移碼的形式存儲。對於單精度浮點數,偏移量爲127(7FH),而雙精度的偏移量爲1023(3FFH)。存儲浮點數的階碼之前,偏移量要先加到階碼上。前面例子中,階爲2的三次方,在單精度浮點數中,移碼後的結果爲127+3即130(82H),雙精度爲1026(402H)。
浮點數有兩個例外。數0.0存儲爲全零。無限大數的階碼存儲爲全1,尾數部分全零。符號位指示正無窮或者負無窮
下面舉幾個例子:
單精度浮點數
十進制 ;規格化 ;符號 ;移階碼 尾數
-12 -1.1x2^3 1 10000010 1000000 00000000 00000000
0.25 1.0x2^-2 0 01111101 0000000 00000000 00000000
所有字節在內存中的排列順序,intel的cpu按little endian順序,motorola的cpu按big endian順序排列。

單精度和雙精度的區別

簡介

單精度和雙精度數值類型最早出現在C語言中(比較通用的語言裏面),在C語言中單精度類型稱爲浮點類型(Float),顧名思義是通過浮動小數點來實現數據的存儲。這兩個數據類型最早是爲了科學計算而產生的,他能夠給科學計算提供足夠高的精度來存儲對於精度要求比較高的數值。
但是與此同時,他也完全符合科學計算中對於數值的觀念:
當我們比較兩個棍子的長度的時候,一種方法是並排放着比較一下,一種方法是分別量出長度。但是事實上世界上並不存在兩根完全一樣長的棍子,我們測量的長度精度受到人類目測能力和測量工具精度的限制。從這個意義上來說,判斷兩根棍子是否一樣長絲毫沒有意義,因爲結果一定是False,但是我們可以比較他們兩個哪個更長或者更短。這個例子很好地概括了單精度/雙精度數值類型的設計初衷和存在意義。
基於上述認識,單精度/雙精度數值類型從一開始設計的時候,就不是一個準確的數值類型,他只保證在他這個數值類型的精度之內是準確的,精度之外則不保證,比方說,一個數值5.1,很可能存儲在單精度/雙精度數值中的實際值是5.100000000001或者5.09999999999999。導致這個現象的原因我們可以通過兩種方式來解釋:

簡單的解釋方法

你可以嘗試在任何一個控件的屬性面板中,設定他的寬度爲:3.2CM,當你輸入完畢後,你會發現值自動變成了3.199cm,無論你怎麼改,你都無法輸入3.200CM,因爲實際上在電腦中存儲的並不是CM爲單位的數值,而是“緹”爲單位的數值,而“緹”和CM之間的比值,是個很難被除盡的數,因此你輸入完畢後,電腦自動轉換成了最接近的“緹”值,然後再轉換成釐米顯示到屬性面板上,這一乘一除,兩次四捨五入,誤差就出來了。單精度/雙精度也是類似的原理,其實在二進制存儲的時候,單精度/雙精度都採用了類似相近分數的方法,而這樣的存儲是不可能做到準確的。

深入的解釋方法

讓我們來看看我們存儲到數字介質中的單精度/雙精度值到底是怎麼樣的,我們使用如下代碼對單精度類型進行一個解剖:
Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any,Source As Any,ByVal Length As Long)
Public Sub floatTest()
Dim dblVar As Single
dblVar = 5.731 / 8
dblOutput dblVar
dblVar = dblVar * 2
dblOutput dblVar
dblVar = dblVar * 2
dblOutput dblVar
dblVar = dblVar * 2
dblOutput dblVar
dblVar = dblVar * 2
dblOutput dblVar
dblVar = dblVar * 2
dblOutput dblVar
End Sub
Public Sub dblOutput(ByVal dblVar As Single)
Dim bytVar⑶As Byte
Dim i As Integer,j As Integer
Dim strVar As String
CopyMemory ByVal VarPtr(bytVar(0)),ByVal VarPtr(dblVar),4
strVar = dblVar & ": "
For i = 3 To 0 Step -1
For j = 7 To 0 Step -1
strVar = strVar & (bytVar(i) And 2 ^ j) / 2 ^ j
Next j
strVar = strVar & " "
Next i
Debug.Print strVar
End Sub
運行後我們得到輸出結果(輸出格式爲高位左,低位右):
.716375: 00111111 00110111 01100100 01011010
⒈43275: 00111111 10110111 01100100 01011010
⒉8655: 01000000 00110111 01100100 01011010
⒌731: 01000000 10110111 01100100 01011010
⒒462: 01000001 00110111 01100100 01011010
22.924: 01000001 10110111 01100100 01011010
這裏,我們把單精度類型轉化成了二進制數據輸出,這裏我們看到,雖然這六個數字完全不同,但是它們的二進制存儲驚人地相似,我們看到紅色標記部分,每次都是加1,事實上,單精度數據類型使用從高位開始第1位作爲正負標記位(綠色),第2位到第9位,是一個跨字節的有符號字節類型數據,這個數值決定了小數點移動的方向和位數(紅色),第10位到32位保存一個整數(藍色)在存儲過程中,電腦首先把輸入的值不斷移位(乘除2)直到這個數的整數部分佔用了全部24位的整數位,然後把移動的位數寫入浮點部分(紅色),而移位後的結果寫入整數部分(藍色和綠色),小數部分則捨棄。求值的時候則是反向過程,先根據正負位和整數位求值,然後根據紅色部分的整數來進行移位(乘除2的次方),最終纔是我們得到的單精度數值。雙精度數值也是同樣原理,只是位數更多而已。

評價


通過解剖單精度數值的二進制存儲格式,我們可以清楚看到,實際上單精度/雙精度的存儲,都要通過乘法和除法,其中必有舍入,如果恰好你的數值在除法中被舍入了,那麼你賦的初值就很可能與你最終存儲的值不完全相同,其中的微小差異,並不與單精度/雙精度的設計目標相違背。
當我們在數據庫中或者VBA代碼中使用一個單精度/雙精度數值的時候,也許你從界面上看不到區別,但是在實際的存儲中,這個差別卻真真切切地就在那裏,當你對其進行相等比較的時候,系統只是簡單地作二進制的比較,界面上無法體現的微小差異,在二進制比較面前卻無處遁形,於是,你的等於比較返回了一個意料之外的False。

發佈了53 篇原創文章 · 獲贊 15 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章