3. 为什么 0.1 + 0.2 != 0.3
总结
- 底层是二进制实现
概述
在 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