JavaScript - 浮點數計算之 0.1 + 0.2 !== 0.3
0.1 + 0.2 等於 ?
0.30000000000000004
因此 0.1 + 0.2 === 0.3
為 false
為什麼會不精準 ?
JavaScript 的浮點數計算採用 IEEE 二進位浮點數算術標準 ( IEEE 754 ) 中的雙精確度 ( 64 位元 ) 浮點數。
info
( 維基百科 - IEEE 754 ) IEEE 二進位浮點數算術標準(IEEE 754)是 20 世紀 80 年代以來最廣泛使用的浮點數運算標準,標準中定義了表示浮點數的格式,包含負零、反常值 ( denormal number )、特殊數值 ( 無窮值 Inf、非數值 NaN ),以及這些數值的「浮點數運算子」;它也指明了四種數值修約規則和五種例外狀況(包括例外發生的時機與處理方式)。
0.1
與 0.2
在轉換為二進位實際儲存的值再轉換為十進位後會比原先略大 ( 這裡就已經損失精度 ),在二進位運算後得到的結果就會是 0.30000000000000004
。
但有時候在 JavaScript 運算又會得到理想的數值,例如 0.5 + 0.625
會精準得到 1.125
,原因是二進位能精確表示「位數有限且分母為 2 的倍數的小數」,因此 0.5
、0.25
、0.125
、0.625
等都可以被轉換為精確的二進位儲存。
不過,運算小數明明不是位數有限且分母為 2 的倍數的小數,卻有可能獲得正確的計算結果,例如 0.1
+ 0.3
會正確得到 0.4
,由於 IEEE 754 規則會將浮點數截斷,所以是「有可能」會在浮點數計算中得到預期結果。
tip
所以在判斷式中盡量不要直接比較兩個浮點數,很容易遇到非預期結果。
如何修正 ?
JS 原生方法
.toFixed
(0.1 + 0.2).toFixed(1) // '0.3'
(0.2 + 0.4).toFixed(1) // '0.6'
.toPrecision
(0.1 + 0.2).toPrecision(1) // '0.3'
(0.2 + 0.4).toPrecision(1) // '0.6'
但使用以上兩種方法仍然不是最安全的,可以看以下例子 :
0.15.toFixed(1) // '0.1'
0.15.toPrecision(1) // '0.1'
0.25.toFixed(1) // '0.3'
0.25.toPrecision(1) // '0.3'
當想要做四捨五入時還是會因為浮點數精確度問題造成非預期結果。
第三方套件
- Math.js
math.format(0.1 + 0.2, {precision: 14}) // '0.3'
math.equal(0.1 + 0.2, 0.3) // truemath.format(0.15, { precision: 1 }) // '0.2'
math.format(0.25, { precision: 1 }) // '0.3' - decimal.js
let x = new Decimal(0.1)
let ans = x.add(0.2)
ans.toString() // '0.3'
ans.equals(0.3) // truelet a = new Decimal(0.15)
let b = new Decimal(0.25)
a.toFixed(1) // '0.2'
a.toPrecision(1) // '0.2'
b.toFixed(1) // '0.3'
b.toPrecision(1) // '0.3' - bignumber.js
let x = new BigNumber(0.1)
let ans = x.plus(0.2)
ans.toString() // '0.3'
ans.isEqualTo(0.3) // truelet a = new BigNumber(0.15)
let b = new BigNumber(0.25)
a.toFixed(1) // '0.2'
a.toPrecision(1) // '0.2'
b.toFixed(1) // '0.3'
b.toPrecision(1) // '0.3'
Reference
- 在 JavaScript 中 0.1 + 0.2 會是多少? 為什麼? 如何避免相關問題? - 軟體工程師面試、職涯、新加坡生活分享 - ExplainThis
- IEEE-754 與浮點數運算 - iT 邦幫忙::一起幫忙解決難題,拯救 IT 人的一天
- JavaScript / 程式語言中,0.1 + 0.2 != 0.3
- JavaScript 关于 IEEE 754 双精度浮点数的实现 | 风动之石的博客
- 0.1 + 0.2不等于0.3?为什么JavaScript有这种"骚"操作?
- JavaScript 裡的概數運算,IEEE 754 的標準其實沒有被實作?
- Number.prototype.toPrecision() - JavaScript | MDN
- Number.prototype.toFixed() - JavaScript | MDN