為何二進位浮點數不準確?IEEE 754 二進位浮點數拾入規則
訂閱 480
本節使用的「不準確」一詞可能會被解釋為「精確度丟失」,如果從二進位的角度來講,一些無限小數的數位的確丟失了,但從開發人員的角度看,數值更像是失去了準確性。
二進位浮點數不準確
二進位浮點數存在不準確的情況,尤其在計算的結果中,你可能會看到意想不到的數值。讓我們來看一個經典的案例,在 JavaScript 中計算0.1與0.2的和,輸出結果並不是理所當然的0.3,而是0.30000000000000004。
console.log(`0.1 + 0.2 = ${parseFloat('0.1') + parseFloat('0.2')}`)0.1 + 0.2 = 0.30000000000000004上面的範例說明,浮點數0.1,0.2,0.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?!
當然,你也可以把不準確的原因,歸於人們不習慣使用由0和1組成的二進位數值,一旦沒有了十進位和二進位之間的轉換,也就沒有了不準確的問題。
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,否則同樣直接丟棄,這將保證無論是正數還是負數都會更接近負無限。