深入理解計算機系統(十二):浮點數舍入以及運算

深入理解計算機系統(十二):浮點數舍入以及運算

4 人贊了文章

目錄

1、舍入

2、浮點運算

3、總結

  上一篇博客我們講解了二進位小數如何表示以及IEEE浮點標準。而且我們也提到過因為這種表示方法限制了浮點數的範圍和精度,浮點數只能近似的表示一個數。

  比如 數字1/5,我們能用十進位小數 0.2 準確的表示,但是我們卻不能把它準確的表示為一個二進位小數,我們只能通過增加二進位表示的長度來提高表示的精度。如下:

那我們該怎麼辦呢?

正文:

1、舍入

  對於不能精確的表示的數,我們採取一種系統的方法,找到「最接近」的匹配值,它可以用期望的浮點形式表現出來,這就是舍入。

舍入一共有四種方式,分別是向偶數舍入、向零舍入、向上舍入以及向下舍入

  可以看下面的例子:

  向偶數舍入,是將數字向上或向下舍入,使得結果的最低有效數字是偶數;而向零舍入則是向靠近零的值舍入;向上舍入則是向比它大的方向靠近;向下舍入則是向比它小的方向靠近。

  這四個我們可以用一個直角坐標系來理解:

  除了向偶數舍入以外,其它三種方式都會有明確的邊界。這裡的含義是指這三種方式舍入後的值x與舍入之前的值x會有一個明確的大小關係,比如對於向上舍入來說,則一定有x <= x。對於向零舍入來說,則一定有|x| >= |x|。

  那麼我們什麼時候會使用向偶數舍入呢?

  1、比如舍入一組數值,計算這些值的平均數中引入統計偏差,如果向上舍入,那麼得到的平均值會比這些數本身的平均值略高;向下舍入,則會偏低。而向偶數舍入則會避免這種偏差,在50%的時間內,它向上舍入,剩下50%的時間內,它向下舍入。

  2、在我們不想舍入到整數時,我們只是簡單的考慮最低有效數字是奇數還是偶數。

通常情況下我們採取的舍入規則是在原來的值是舍入值的中間值時,採取向偶數舍入,在二進位中,偶數我們認為是末尾為0的數。而倘若不是這種情況的話,則一般會有選擇性的使用向上和向下舍入,但總是會向最接近的值舍入。其實這正是IEEE採取的默認的舍入方式,因為這種舍入方式總是企圖向最近的值的舍入。

2、浮點運算

  在IEEE標準中,制定了關於浮點數的運算規則,就是我們將把兩個浮點數運算後的精確結果的舍入值,作為我們最終的運算結果。正是因為有了這一個特殊點,就會造成浮點數當中,很多運算不滿足我們平時熟知的一些運算特性。

  我們可以先看下面這段程序輸出結果:

public void testFloat(){ float f1 = 3.14f + 10000000000f - 10000000000f; float f2 = 3.14f + (10000000000f - 10000000000f); System.out.println(f1); System.out.println(f2); }

  結果都是 3.14 嗎?

  我們看到 f1 的值是0,f2的值才是3.14。為什麼呢?這是因為前面3.14f+10000000000f 時,會將 3.14 這個有效數值舍入掉,而導致最終結果為0.0

  f2 由於括弧的存在,會先進行括弧裡面的運算,結果是0,然後在與3.14相加。

  也就是浮點運算不滿足加法的結合律 a + b + c != a + (b + c)。同時乘法結合律也不滿足:a * b * c != a * (b * c);還要分配律也不滿足: a * (b + c) != a * b + a * c

  浮點數失去了很多運算方面的特性,因此也導致很多優化手段無法進行,比如我們試圖優化下面這樣一段程序。

/* 優化前 */ float x = a + b + c; float y = b + c + d; /* 編譯器試圖省去一個浮點加法 */ float t = b + c; float x = a + t; float y = t + d;

  上面優化前是進行了四次浮點運算,而編譯器優化後只需要進行三次浮點運算。但是這中間的 x 可能回產生與原始值不同的值,因為它使用了加法運算不同的結合方式。所以現在的編譯器都傾向於保守的方式,避免任何對功能產生的優化,即使是很輕微的影響。

  另外,浮點加法滿足單調性屬性:如果 a>=b,那麼對於任何a、b以及 x 的值,除了 NaN,都有 x+a >= x+b。無符號或者補碼加法不具有這個實數(和整數)加法的屬性。

3、總結

  好了,那麼到此《深入理解計算機系統》前面兩章的內容我們就結束了,這裡我們主要需要了解無符號和補碼編碼格式,以及它們的運算。然後擴展到整數的表示和運算,實數的表示和運算,在實際編程中,我們會經常和數打交道,如何避免一些錯誤,相信看完後會有個大概的了解了。那麼接下來我們將學習第三章,這將是一個全新的世界——彙編語言。這肯定比我們前面講的要有趣多了,前面都是和0或者1這樣的數字打交道,後面至少是一種編程語言,相信會更加有趣。


推薦閱讀:

PS知識知多少?PS智能對象不得不知的十件事(課課家)
深入理解計算機系統(三):存儲設備
給編程初學者的一些建議
為什麼說會使用搜索引擎對學習編程很重要
原來,計算思維該這樣理解

TAG:計算機原理 | 計算機 | 編程 |