淺剖0.1 + 0.2 = ?

HappyCodingTop發表於2022-07-09

溯源

數字計算機當中是以二進位制儲存,所以這裡我們需要先知道十進位制轉二進位制的規則,和二進位制轉十進位制的規則
十進位制轉換二進位制
分兩種規則:

  1. 整數轉二進位制:除2取餘,逆序排列。
  2. 小數轉二進位制:乘2取整,正序排列

怎麼理解這兩句:比如 9.375這個數字,整數是9,小數是0.375

整數9轉成二進位制就是: 1001
image.png

那麼小數0.375轉成二進位制就是:011
image.png
那麼9.375轉成二進位制就是 1001.011

可利用下面方法驗證

console.log((9.375).toString(2))
console.log(Number.prototype.toString.call(9.375,2));
console.log(Number.prototype.toString.call(Number(9.375),2));

image.png

同樣二進位制轉十進位制

  1. 小數點前:從右往左用二進位制的每個數乘以2的相應次方遞增
    image.png
  2. 小數點後:從左往右用二進位制的每個數乘以2的相應負次方遞增
    image.png

IEEE 754 雙精度64位浮點數
image.png
十進位制有個叫科學計數法表示:
image.png
image.png

所以

0.1的二進位制
e = -4;
m = 1.1001100110011001100110011001100110011001100110011010 (52位)
image.png
0.2的二進位制
e = -3;
m = 1.1001100110011001100110011001100110011001100110011010 (52位)
然後我們把它相加,這裡有一個問題,就是指數不一致時,一般是往右移,因為即使右邊溢位了,損失的精度遠遠小於左移時的溢位
所以轉化後為

e = -3; m = 0.1100110011001100110011001100110011001100110011001101 (52位)
+
e = -3; m = 1.1001100110011001100110011001100110011001100110011010 (52位)

得到

e = -3; m = 10.0110011001100110011001100110011001100110011001100111 (52位)

保留一位整數

e = -2; m = 1.00110011001100110011001100110011001100110011001100111 (53位)

超過了52位,做四捨五入
舍入與原來的數最接近的,原則是保留偶數最終的二進位制數

1.0011001100110011001100110011001100110011001100110100 * 2 ^ -2

=0.010011001100110011001100110011001100110011001100110100

轉化為十進位制為0.30000000000000004

如何避免這個事情的發生

我們認為可以理解的數,在計算機內部它反而是一個無限迴圈的小數,
它沒有辦法,它的尾數的尾數只有52位,它只有截斷,進行規格化,進行舍入處理的時候可能就會導致一個精度的偏差,所以得到的是一個相對精準的結果但不是絕對的精準
有什麼辦法讓0.1+0.2等於0.3
(0.2*100 + 0.1*100)/100
那就是避免計算的時候出現小數位

相關文章