技術 : 通信工程 > 單精度
目錄
單精度介紹
  單精度數,是指計算機表達實數近似值的一種方式。vb中,single(單精度浮點型)變量存儲為 ieee 32 位(4 個字節)浮點數值的形式,它的範圍在負數的時候是從 -3.402823e38 到 -1.401298e-45,而在正數的時候是從 1.401298e-45 到 3.402823e38 。
單精度存儲格式
  符號位s(sign) - 1bit
   0代表正號,1代表負號。(+0、-0視為相同?(歡迎補充資料))
   指數位e(exponent) - 8bit
   e的取值範圍為0-255(無符號整數),實際數值e=e-127。
   有時e也稱為“移碼”,或不恰當的稱為“階碼”(階碼實際應為e)
   尾數位m(mantissa) - 23bit
   m也叫有效數字位(sinificand)、係數位(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,所以s=s-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 |- 6.5 0 10000001 10100000000000000000000 |- -6.5 1 10000001 10100000000000000000000 |}
單精度浮點數的表示範圍及說明
  表示範圍
   最大表示範圍:單精度浮點數可以表示的範圍為±3.40282 * 1038(1.1111...12*2127)
   接近於0的最小值:單精度浮點數可以表示1.175 * 10-38(1.00...02*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的更多規範
  0值
  以指數e、尾數m全零來表示0值。當指數位s變化時,實際存在“正0”和“負0”兩個內部表示,其值認為都等於0。
   接近於0的數值
   當指數e為0時,ieee 754規定:m=(0.m)2 e=-126。通過這個規則,將擴大對0值附近數據的表示能力。
   幾個實例:
   符號s 指數e 尾數m 代表意義 對應10進製 相對誤差0 00000000 100000000000000000000000.12*2-1265.87e-392-23
  0 00000000 010000000000000000000000.012*2-1262.94e-392-22
  ……………………
  0 00000000 000000000000000000000100.0000...12*2-126=2-1482.80e-4550%
  0 00000000 000000000000000000000012-1491.40e-45100%
  (註:部分係統不支持此類數據,而直接進行0值處理)
無窮大和nan
  當指數e為全1時,ieee 754規定此類存儲作為特別使用,而不是普通數據。
   無窮大
   e=255 m=0時,用作無窮大(或infinity、∞)。根據符號不同,又有+∞、-∞。
   無窮大可以由算術運算得出,下面是有關無窮大的幾個運算示例:
   1/∞ = 0, -1/∞ = -0, 1/0 = ∞, -1/0 = -∞ </pre>
   nan
   e=255、m不為0時,用作nan(not a number,“不是數”之意)。
   當對數據進行非法運算(例如對-1開平方)時,結果出現nan。
   運算時含有nan時,結果也必定是nan。
   註意:nan<>nan!對nan進行相互比較是無意義的。
   (nan還有qnan和snan的用法,用於程序捕獲某些例外狀態。參見nan條目)
不同實現中的單精度存儲變形
  單精度浮點數應用廣泛,而一些低成本的單片機係統中不具備數學運算的協處理器硬件,因而在在不同係統中,根據硬件特性對浮點數的軟件實現進行了相應調整和簡化。存在如下一些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 10000000000000000000000
  0.25 1.0x2^-2 0 01111101 00000000000000000000000
  所有字節在內存中的排列順序,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(3) 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
  1.43275: 00111111 10110111 01100100 01011010
  2.8655: 01000000 00110111 01100100 01011010
  5.731: 01000000 10110111 01100100 01011010
  11.462: 01000001 00110111 01100100 01011010
  22.924: 01000001 10110111 01100100 01011010
  這裏,我們把單精度類型轉化成了二進製數據輸出,這裏我們看到,雖然這六個數字完全不同,但是它們的二進製存儲驚人地相似,我們看到紅色標記部分,每次都是加1,事實上,單精度數據類型使用從高位開始第1位作為正負標記位(緑色),第2位到第9位,是一個跨字節的有符號字節類型數據,這個數值决定了小數點移動的方向和位數(紅色),第10位到32位保存一個整數(藍色)在存儲過程中,電腦首先把輸入的值不斷移位(乘除2)直到這個數的整數部分占用了全部24位的整數位,然後把移動的位數寫入浮點部分(紅色),而移位後的結果寫入整數部分(藍色和緑色),小數部分則捨棄。求值的時候則是反嚮過程,先根據正負位和整數位求值,然後根據紅色部分的整數來進行移位(乘除2的次方),最終纔是我們得到的單精度數值。雙精度數值也是同樣原理,衹是位數更多而已。
  通過解剖單精度數值的二進製存儲格式,我們可以清楚看到,實際上單精度/雙精度的存儲,都要通過乘法和除法,其中必有捨入,如果恰好你的數值在除法中被捨入了,那麽你賦的初值就很可能與你最終存儲的值不完全相同,其中的微小差異,並不與單精度/雙精度的設計目標相違背。
  當我們在數據庫中或者vba代碼中使用一個單精度/雙精度數值的時候,也許你從界面上看不到區別,但是在實際的存儲中,這個差別卻真真切切地就在那裏,當你對其進行相等比較的時候,係統衹是簡單地作二進製的比較,界面上無法體現的微小差異,在二進製比較面前卻無處遁形,於是,你的等於比較返回了一個意料之外的false。
無窮大和NaN
  當指數E為全1時,IEEE 754規定此類存儲作為特別使用,而不是普通數據。
  無窮大
  E=255 M=0時,用作無窮大(或Infinity、∞)。根據符號不同,又有+∞、-∞。
  無窮大可以由算術運算得出,下面是有關無窮大的幾個運算示例:
  1/∞ = 0, -1/∞ = -0, 1/0 = ∞, -1/0 = -∞ </pre>
  NaN
  E=255、M不為0時,用作NaN(Not a Number,“不是數”之意)。
  當對數據進行非法運算(例如對-1開平方)時,結果出現NaN。
  運算時含有NaN時,結果也必定是NaN。
  註意:NaN<>NaN!對NaN進行相互比較是無意義的。
  (NaN還有QNaN和SNaN的用法,用於程序捕獲某些例外狀態。參見NaN條目)
包含詞
單精度型讀單精度寫單精度
單精度介紹單精度整數單精度計算
單精度字段單精度浮點數單精度浮點型
單精度存儲格式單精度浮點數實例單精度和雙精度的區別