在JavaScript開發中,浮點數精度問題是一個常見的陷阱。本文將深入探討JavaScript中浮點數精度問題的原因、影響以及解決方案。
一、浮點數精度常見問題
(一)加法運算
console.log(0.1 + 0.2); // 0.30000000000000004
console.log(0.7 + 0.1); // 0.7999999999999999
console.log(0.2 + 0.4); // 0.6000000000000001
console.log(2.22 + 0.1); // 2.3200000000000003
(二)減法運算
console.log(1.5 - 1.2); // 0.30000000000000004
console.log(0.3 - 0.2); // 0.09999999999999998
(三)乘法運算
console.log(19.9 * 100); // 1989.9999999999998
console.log(19.9 * 10 * 10); // 1990
console.log(9.7 * 100); // 969.9999999999999
console.log(39.7 * 100); // 3970.0000000000005
(四)除法運算
console.log(0.3 / 0.1); // 2.9999999999999996
console.log(0.69 / 10); // 0.06899999999999999
(五)四舍五入保留小數位數
console.log((1.335).toFixed(2)); // 1.33
二、為什么會有這樣的問題
(一)浮點數的存儲方式
在JavaScript中,所有數字都以64位浮點數形式存儲,即使整數也是如此。這種存儲方式基于IEEE 754標準,其中64位的劃分如下:
- 符號位(1位):表示正負數,0表示正數,1表示負數。
- 指數位(11位):表示次方數。
- 尾數位(52位):存儲小數部分,超出部分自動進一舍零。
(二)二進制表示的局限性
浮點數在計算機中以二進制形式存儲,但某些十進制小數無法精確表示為二進制小數。例如,0.1和0.2在二進制中是無限循環小數,因此在存儲時會被截斷,導致精度丟失。
(三)JavaScript的Number類型
JavaScript中的Number類型統一按浮點數處理,整數也是按最大54位來計算的。因此,當數值超過安全范圍(Number.MAX_SAFE_INTEGER
和Number.MIN_SAFE_INTEGER
)時,就會出現精度問題。
三、解決方案
(一)使用第三方庫
為了精確處理浮點數運算,可以使用一些成熟的第三方庫,如Math.js
、decimal.js
和big.js
。
Math.js
Math.js
是一個功能強大的數學庫,支持多種數據類型和操作。
const math = require('mathjs');
console.log(math.add(0.1, 0.2)); // 0.3
decimal.js
decimal.js
提供任意精度的十進制數值運算。
const Decimal = require('decimal.js');
console.log(new Decimal(0.1).plus(0.2).toNumber()); // 0.3
big.js
big.js
專注于大數運算,支持高精度的浮點數運算。
const Big = require('big.js');
console.log(new Big(0.1).plus(0.2).toString()); // "0.3"
(二)自定義函數
如果不想引入第三方庫,可以編寫自定義函數來處理浮點數運算。以下是一個簡單的加法函數示例:
function add(a, b) {const factor = 10 ** 10;return (Math.round(a * factor) + Math.round(b * factor)) / factor;
}
console.log(add(0.1, 0.2)); // 0.3
(三)避免浮點數運算
在某些情況下,可以避免直接使用浮點數運算。例如,處理貨幣時,可以將金額轉換為整數(如分)進行計算,最后再轉換回浮點數。
function addCents(a, b) {return (Math.round(a * 100) + Math.round(b * 100)) / 100;
}
console.log(addCents(0.1, 0.2)); // 0.3