關於浮點數的一些常見要點

關於浮點數的一些常見要點


本來這裏應該有一篇關於浮點數的介紹的文章,不過個人認爲書裏面寫得還是比較完善了,就暫時想欠着了,而這些關鍵點書裏面一般介紹的比較少,我個人認爲應該單獨寫文章出來


浮點數的不精確性

正如絕大多數的文章都會告訴你的一樣,浮點數都是不精確的,所有浮點數都無法精確地表達實數.舉一個例子,比如.0.01 在單精度浮點型中可以用說是0.01000000000298023226097399174250313080847263336181640625.進行表示.因此對於絕大多數的語言而言,浮點數的使用相等==使用嚴重的問題.通常這一類的文檔都會給出建議使用相減後的絕對值在某一個區間進行表示某兩個浮點數相等的判斷.

<?php
$a = 1.23456789;
$b = 1.23456780;
$epsilon = 0.00001;

if(abs($a-$b) < $epsilon) {
    echo "true";
}
?>

遠不要相信浮點數結果精確到了最後一位,也永遠不要比較兩個浮點數是否相等

浮點數的四則運算

設兩個浮點數 X=Mx2Ex ,Y=My2Ey

實現X±Y要用如下5步完成:

  • 對階操作:小階向大階看齊
  • 進行尾數加減運算
  • 規格化處理:尾數進行運算的結果必須變成規格化的浮點數,對於雙符號位(就是使用00表示正數,11表示負數,01表示上溢出,10表示下溢出)的補碼尾數來說,就必須是
    001×××…×× 或110×××…××的形式
    若不符合上述形式要進行左規或右規處理.
  • 舍入操作:在執行對階或右規操作時常用“0”舍“1”入法將右移出去的尾數數值進行舍入,以確保精度.
  • 判結果的正確性:即檢查階碼是否溢出

若階碼下溢(移碼錶示是00…0),要置結果爲機器0;
若階碼上溢(超過了階碼錶示的最大值)置溢出標誌.

現在用一個具體的例子來說明上面的5個步驟

例題:假定X=0 .0110011211,Y=0.11011012-10(此處的數均爲二進制), 計算X+Y;

首先,我們要把這兩個數變成2進製表示,對於浮點數來說,階碼通常用移碼錶示,而尾數通常用補碼錶示.

要注意的是-10的移碼是00110
[X]浮: 0 1 010 1100110
[Y]浮: 0 0 110 1101101
符號位 階碼 尾數

  • 求階差:│ΔE│=|1010-0110|=0100

  • 對階:Y的階碼小,Y的尾數右移4位
    [Y]浮變爲 0 1 010 0000110 1101暫時保存

  • 尾數相加,採用雙符號位的補碼運算
    00 1100110
    +00 0000110
    00 1101100

  • 規格化:滿足規格化要求

  • 舍入處理,採用0舍1入法處理

故最終運算結果的浮點數格式爲: 0 1 010 1101101

即X+Y=+0. 1101101*210

浮點數的乘除法

  • 階碼運算:階碼求和(乘法)或階碼求差(除法)
    即 [Ex+Ey]移= [Ex]移+ [Ey]補
    [Ex-Ey]移= [Ex]移+ [-Ey]補
  • 浮點數的尾數處理:浮點數中尾數乘除法運算結果要進行舍入處理

例題:X=0 .0110011211,Y=0.11011012-10 求X*Y

解:[X]浮: 0 1 010 1100110
[Y]浮: 0 0 110 1101101

  • 階碼相加
    [Ex+Ey]移=[Ex]移+[Ey]補=1 010+1 110=1 000
    1 000爲移碼錶示的0

  • 原碼尾數相乘的結果爲:
    0 10101101101110

  • 規格化處理:已滿足規格化要求,不需左規,尾數不變,階碼不變.

  • 舍入處理:按舍入規則,加1進行修正

所以 X※Y= 0.1010111*20

浮點數運算的思考

  • 浮點數的精度丟失在每一個表達式,而不僅僅是表達式的求值結果.floor((0.1 + 0.7) * 10) 通常會返回 7 而不是預期中的 8,因爲該結果內部的表示其實是類似 7.9999999999999991118…因此使用浮點數計算會比較危險,寫代碼的時候請謹慎.
  • 在數學上相等的兩個計算序列可以產生不同的浮點值
  • 檢查除數不爲零並不能保證除法不會溢出.
  • 儘管如前所述,IEEE 754的各個算術運算保證精確到ULP的一半以內,但更復雜的公式可能由於四捨五入而遭受更大的誤差。如果問題或其數據是病態的,那麼精度的損失可能很大,這意味着正確的結果對其數據中的微小擾動過敏。然而,如果使用對該數據在數值上不穩定的算法,即使是良好條件的函數也會遭受很大的精度損失:編程語言中表達式的明顯等同表達式的數值穩定性可能顯着不同。消除這種精度損失風險的一種方法是設計和分析數值穩定的算法,這是數學分支的一個目標,稱爲數值分析.大多數語言都會提供某些任意精度庫來給碼農來使用
  • 轉換爲整數並不直觀:轉換(63.0 / 9.0)到整數產生7,但轉換(0.63 / 0.09)可能會產生6.這是因爲轉換通常是截斷而不是舍入. 地板和天花板功能可以產生從直觀預期值中偏離一個的答案.

int float double的轉化

  • 從int轉換成float,數字不會溢出,但是可能被舍入.
  • 從int或float轉換成double,因爲double有更大的範圍(也就是可表示值的範圍),也有更高的精度(也就是有效位數),所以能夠保留精確的數值.
  • 從double轉換成float,因爲範圍要小一些,所以值可能溢出成正無窮或負無窮.另外,由於精確度較小,它還可能被舍人.
  • 從float或 者double轉換成int,值將會向零舍入.例如,1.999將被轉換成1,而-1.999將被轉換成-1.進一步來說,值可能會溢出.C語言標準沒有對這種情況指定固定的結果.與Intel兼容的微處理器指定位模式[10*"00] (字長爲w時的爲整數不確定( integer indefinite) 值. 一 個從浮點數到整數的轉換,如果不能爲該浮點數找到一個合理的整數近似值,就會產生這樣一個值.因此,表達式(int>+lelO會得到-21483648,即從一個正值變成了一個負值.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章