總結
- 底層是二進制實現
概述
在 JavaScript 中,0.1 + 0.2
的結果并不是精確的 0.3
,而是 0.30000000000000004
。這個現象并不是 JavaScript 的“bug”,而是由于浮點數在計算機底層的二進制表示方式導致的精度丟失問題。
一、計算機如何表示小數?
JavaScript 使用 IEEE 754 標準中的 64 位雙精度浮點數(double) 來表示數字。這種格式將一個數字分為三部分:
- 符號位(Sign bit):1 位,表示正負。
- 指數位(Exponent):11 位,表示數值范圍。
- 尾數位(Mantissa / Fraction):52 位,表示精度。
由于計算機只能使用有限位數的二進制來表示小數,因此某些十進制小數無法被精確表示為二進制浮點數。
二、為什么 0.1
和 0.2
無法精確相加?
1. 十進制轉二進制的小數部分是無限循環
0.1
(十進制) →0.00011001100110011...
(二進制,無限循環)0.2
(十進制) →0.0011001100110011...
(二進制,無限循環)
由于只能保留有限位(52 位尾數),因此只能近似存儲這些值。
2. 計算時產生誤差累積
當計算機將這兩個近似值相加時,誤差也隨之累積,最終結果為:
0.1 + 0.2;
// 輸出:0.30000000000000004
三、IEEE 754 表示舉例
以 0.1
為例,其 IEEE 754 表示如下(簡化):
部分 | 值 |
---|---|
符號位 | 0(正數) |
指數位 | -4(即 2^-4) |
尾數位 | 1.10011001100110011001101…(截斷后) |
最終得到的值是:1.10011001100110011001101 × 2^-4
,并不是精確的 0.1
。
四、其他語言也存在這個問題嗎?
是的,所有使用 IEEE 754 浮點數標準的語言都存在這個問題,例如:
- Python
- Java
- C++
- Go
>>> 0.1 + 0.2
0.30000000000000004
五、如何解決精度問題?
1. 使用 toFixed()
+ parseFloat()
parseFloat((0.1 + 0.2).toFixed(1)) === 0.3;
// true
?? 注意:
toFixed()
返回字符串,需用parseFloat()
轉換回數字。
2. 使用 decimal.js
或 big.js
等庫
對于金融計算或高精度需求,推薦使用第三方庫:
npm install decimal.js
import Decimal from "decimal.js";const result = new Decimal(0.1).plus(new Decimal(0.2));
result.equals(0.3); // true
3. 乘以 10^n 轉換為整數運算
function add(a, b, precision = 10) {return (a * precision + b * precision) / precision;
}add(0.1, 0.2); // 0.3