1. 程式人生 > 其它 >浮點運算的誤差_Assembly 浮點運算

浮點運算的誤差_Assembly 浮點運算

技術標籤:浮點運算的誤差

電子計算機裡的浮點運算和持續精確的數學運算是不同的。數學中,所有的數都可以精確表示。但就如前面的章節所示,在電子計算機裡,許多數不能用有限個位元位來描述。所有的計算都在一定的精度下執行。在這節的例子中,為了簡單化,將使用8位的有效數。

要將兩個浮點數相加,它們的指數必須是相等的。如果它們並不相等,那麼通過移動較小指數的數的有效數來使它們相等。例如:考慮10.375 +6.34375 = 16.71875或在十進位制中: 500879090710d0d66c3af6d8df4bb71b.png 這兩個數字的指數不一樣,所以通過移動有效數使指數相同,然後再相加: 34df7f425219f4f52348063ffb9c1e25.png 注意,移位丟掉了 8d422c25b9a7f6a542143a3f0caaddbb.png 中的末尾的1,經過四捨五入後得 69c184e78017d9aa4e110bbb9fe6d271.png 090cdaec80590e07385665b2caa3ff02.png 。加法的結果, b737a84902e09ac16d0a4b2f080df2c6.png
等於10000.110₂或16.75。這個數並不等於準確的答案(16.71875)!它只是一個近似值,是在進行加法操作時四捨五入後的應有誤差。 認識到在電子計算機(或計算器)裡的浮點運算得到的結果經常是近似值是非常重要的。對於電子計算機裡的浮點運算,算術法則不總是對的。算術中假定的無窮精度是任何電子計算機都無法做的。例如,算術法則告訴我們(a + b) - b = a;但是,在電子計算機裡,並不能完全保證它正確。

減法

減法和加法一樣運作,而且有和加法一樣的問題。作為一個例子,考慮16.75 - 15.9375 = 0.8125: 773341ea1784711e608ab68d1be06b01.png 移位1.1111111 X 2³後得到(四捨五入) 1.0000000 X 39d765280ee9084692c8785384afc3d3.png
44ede9892546ae3cb7919de1c3672f4c.png 0.0000110 X 39d765280ee9084692c8785384afc3d3.png = 0.11₂ = 0.75 它並不完全正確的。

乘法和除法

對於乘法,有效數執行乘法操作而指數執行相加操作。考慮10.375 X 2.5 = 25.9375: dc6333414347ec0fd3dda5c302e6755e.png 當然,真正的結果需四捨五入成8位,得: 1.1010000 X 39d765280ee9084692c8785384afc3d3.png = 11010.0002 = 26 除法更復雜,但是也有同樣的四捨五入的誤差問題。

分支程式設計

這一節的重點是浮點運算的結果並不準確。程式設計師必須意識到這點。一個程式設計師經常犯的浮點運算錯誤就是在假定一個運算是精確的情況下,用它們去比較。例如,考慮一個執行復雜運算的f(x)函式和一個求這個函式的根的程式。你可能會試圖用下面的語句來檢查x是不是一個根: if ( f (x) == 0.0 ) 但是,如果f (x)返回1X 1cb096edf517d2a549d47110e0052ed2.png
又該怎麼辦呢?這個數的最合適的含義是x是一個實根的非常好的近似值。可能沒有一個IEEE浮點值x能恰好返回0,因為f (x)的四捨五入誤差。 一個比較好的方法是使用:
if ( fabs( f (x)) < EPS )
其中的EPS是一個巨集,定義為一個非常小的正數(比如說 d9c1e74f3cbe2816f6875e1e74fae41b.png )。當f (x)非常接近0時,它就為真。一般來說,一個浮點數(譬如x)和另一個浮點數(y)的比較,需使用:
if ( fabs(x ¡ y)/fabs(y) < EPS )

4c9e13677ee666ff3f154269038f35f1.png