前言
在使用JavaScript計算浮點數時,會出現精度丟失的問題,其實不止是JavaScript,只要是遵循IEEE 754標準的語言都存在這個問題
為什麼會精度丟失?
表示範圍:
浮點數使用二進制表示,某些十進制數無法精確表示。例如,0.1 和 0.2 在二進制中是無限迴圈小數,因此只能近似表示。精度限制:
浮點數在計算機中是以固定的位數儲存的。對於雙精度浮點數(如 JavaScript 中的Number
型別),它通常使用64位,其中包括符號位1位、指數位11位和尾數位53位。這種限制導致了某些運算的結果無法精確表示。累積誤差:
在進行多次浮點運算時,微小的誤差會逐漸累積,導致最終結果遠離預期值。
0.1 + 0.2 === 0.3?
答案是錯誤的
const a = 0.1; const b = 0.2; console.log(a + b); // 0.30000000000000004
這就是由於精度丟失的問題
其中0.1轉換為二進制的過程為
將 0.1 轉換為二進制的過程如下:
0.1 × 2 = 0.2 → 整數部分為 0
0.2 × 2 = 0.4 → 整數部分為 0
0.4 × 2 = 0.8 → 整數部分為 0
0.8 × 2 = 1.6 → 整數部分為 1
0.6 × 2 = 1.2 → 整數部分為 1
0.2 × 2 = 0.4 → 整數部分為 0
...
如此迴圈下去,0.1 在二進制中表現為 0.00011001100110011...
,這是一個無限迴圈的小數。
0.2同理,所以他們相加得到的二進制數也是個無限迴圈的小數,這也就是造成進度丟失的根本原因
解決方式
將其變為整數再相加
function add(num1, num2) { // 將輸入的數字轉換為字串,找到小數位數 const str1 = num1.toString(); const str2 = num2.toString(); const decimalPlaces1 = str1.includes('.') ? str1.split('.')[1].length : 0; const decimalPlaces2 = str2.includes('.') ? str2.split('.')[1].length : 0; // 計算最大的位數 const maxDecimalPlaces = Math.max(decimalPlaces1, decimalPlaces2); // 將數字轉換為整數 const factor = Math.pow(10, maxDecimalPlaces); const intNum1 = Math.round(num1 * factor); const intNum2 = Math.round(num2 * factor); // 進行加法運算 const sum = intNum1 + intNum2; // 將結果轉換回浮點數 return sum / factor; } // 示例使用 console.log(add(0.1, 0.2)); // 輸出: 0.3 console.log(add(0.1, 0.3)); // 輸出: 0.4 console.log(add(1.5, 2.3)); // 輸出: 3.8
function add(num1, num2) {
函式定義:定義一個名為
add
的函式,接受兩個引數num1
和num2
,它們都是需要相加的數字。
const str1 = num1.toString(); const str2 = num2.toString();
轉換為字串:將
num1
和num2
轉換為字串格式,以便後續處理小數位數。
const decimalPlaces1 = str1.includes('.') ? str1.split('.')[1].length : 0; const decimalPlaces2 = str2.includes('.') ? str2.split('.')[1].length : 0;
計算小數位數:
使用
includes
方法檢查字串中是否包含小數點.
。如果包含小數點,使用
split
方法將字串分割成兩部分,並獲取小數部分的長度。如果不包含小數點,則小數位數為 0。
const maxDecimalPlaces = Math.max(decimalPlaces1, decimalPlaces2);
確定最大小數位數:使用
Math.max
函式找到兩個數字中小數位數的最大值,以便統一處理。
const factor = Math.pow(10, maxDecimalPlaces);
計算因子:計算
10
的最大小數位數的冪,目的是將浮點數轉換為整數。例如,如果最大小數位數是 2,則因子為 100。
const intNum1 = Math.round(num1 * factor); const intNum2 = Math.round(num2 * factor);
轉換為整數:
將
num1
和num2
乘以因子,得到相應的整數。使用
Math.round
方法確保在轉換過程中不會產生四捨五入的誤差。
const sum = intNum1 + intNum2;
進行加法運算:將兩個整數相加,得到
sum
。
bash程式碼解讀複製程式碼return sum / factor;
返回結果:將總和除以因子,轉換回浮點數,並返回結果。