為何二進位浮點數不準確?IEEE 754 二進位浮點數拾入規則

閱讀 4:39·字數 1395·發佈 
Youtube 頻道
訂閱 133

本節使用的“不準確”一詞可能會被解釋為“有效位數丟失”,如果從二進位的角度來講,一些無限小數的數位的確丟失了,但從開發人員的角度看,數值更像是失去了準確性。

二進位浮點數不準確

二進位浮點數存在不準確的情況,尤其在計算的結果中,你可能會看到意想不到的數值。讓我們來看一個經典的案例,在 JavaScript 中計算0.10.2的和,輸出結果並不是理所當然的0.3,而是0.30000000000000004

floating.js
console.log(`0.1 + 0.2 = ${parseFloat('0.1') + parseFloat('0.2')}`)
0.1 + 0.2 = 0.30000000000000004

上面的範例說明,浮點數0.10.20.3,至少有一個是不準確的,當然,事實上他們每一個都無法準確。

二進位浮點數不準確的原因

二進位浮點數之所以會不準確,是因為我們習慣使用十進位,而十進位與二進位的數值並不是一一對應的,大部分十進位小數沒有對應的二進位小數。

如果十進位數值只有整數部分,那麽沒有問題,你總能找到一個相同的二進位數值,只需要不斷的加1或減1

可一旦擁有小數部分,情況就變得完全不同了。如果把小數理解為對物體的劃分,那麽0.3相當於把物體分為 10 份然後取走其中的 3 份,這樣的事情在二進位中是無法做到的,因為二進位的小數只能將物體分為 2 份,每一份都對應了十進位的0.5,雖然可以將 2 份劃分為 4 份,甚至更進一步的劃分下去,但最終你會發現,二進位沒有等價於十進位0.3的小數,他只能無限的接近0.3,這也就是二進位浮點數不準確的原因。

最低位為 5 的十進位小數擁有對應的二進位小數

大部分十進位小數都找不到對應的二進位小數,除了以 5 結尾的十進位小數以外,比如十進位的0.5對應了二進位0.1。這可以被解釋為,取出 10 份中的 5 份,與取出 2 份中的 1 份是等價的。

都怪人們不使用 0 和 1?!

當然,你也可以把不準確的原因,歸於人們不習慣使用由01組成的二進位數值,一旦沒有了十進位和二進位之間的轉換,也就沒有了不準確的問題。

IEEE 754 二進位浮點數拾入規則

由於儲存空間有限,對於從十進位轉換而來的二進位無限小數,IEEE 754 標準規定需要對其進行拾入,以便能夠放入儲存空間內。

IEEE 754 二進位浮點數就近拾入規則

IEEE 754 定義了四種拾入方式,預設采用就近拾入的方式,他類似於十進位的四舍六入五取偶。當被舍棄部分的最高位為0時不需要進1,這相當於四舍。當被舍棄部分的最高位為1並且後面不全為0時需要進1,這相當於六入。當被舍棄部分的最高位為1且後面全為0時,則根據未舍棄部分的最低位來決定是否進1,最低位為1,需要進1,最低位為0,不需要進1,這相當於五取偶。

假設,二進位小數0.000011000被舍棄的部分是末尾的1000,那麽需要進1,因為未舍棄部分的最低位為1,拾入結果將是0.00010

IEEE 754 二進位浮點數向零拾入,向正無限大拾入,向負無限大拾入規則

除了就近拾入規則,IEEE 754 還定義了向零拾入,向正無限大拾入,向負無限大拾入,他們可用於計算範圍邊界。

向零拾入會直接丟棄需要舍棄的部分,這樣無論是正數還是負數都會更接近於0

向正無限大拾入會直接丟棄負數需要舍棄的部分,如果正數被丟棄的部分含有1則進1,否則同樣直接丟棄,這將保證無論是正數還是負數都會更接近正無限大。

向負無限大拾入會直接丟棄正數需要舍棄的部分,如果負數被丟棄的部分含有1則進1,否則同樣直接丟棄,這將保證無論是正數還是負數都會更接近負無限大。

程式碼

floating.js·codebeatme/programming-reference·GitHub