在上方计算器中输入 0.1 + 0.2,你会看到结果是 0.30000000000000004,而不是你预期的整洁数字 0.3。计算器并没有出错——它显示的是一道特定二进制运算的精确结果。理解这个问题只需不到五分钟,而且会彻底改变你解读任何数字计算器小数结果的方式。
为什么 0.1 无法在二进制中精确存储
计算机内部的所有数字都以二进制形式存储——即以 2 为基数,只使用 0 和 1 两个数字。在十进制(以 10 为基数)中看起来简单的小数,在二进制中往往是无限循环小数,就像 1 ÷ 3 = 0.3333… 在十进制中无限循环一样。
十进制数 0.1 就是这样一个小数。它的二进制表示为 0.0001100110011001100110011…——一个永无止境的循环模式。使用 IEEE 754 双精度标准的计算机(JavaScript 所采用的格式,因此也是所有基于浏览器的计算器所使用的格式)以恰好 64 位来存储数字。这 64 位无法容纳无限长的数字串,因此该值会被舍入到最接近的可表示数。舍入后的结果并不完全等于 0.1,而是:
0.1000000000000000055511151231257827021181583404541015625
0.2 也是如此。它的二进制表示同样是无限循环小数,舍入后变为:
0.200000000000000011102230246251565404236316680908203125
两个舍入值相加时会发生什么
当计算器将这两个不精确的表示值相加时,微小的误差并不会相互抵消——而是叠加在一起。相加的结果为:
0.3000000000000000444089209850062616169452667236328125
该数字舍入到最接近的 64 位浮点数后,变为 0.30000000000000004。第十七位小数处多出的 4 并不是计算器的舍入错误——而是根据 IEEE 754 标准,将两个不精确的二进制近似值相加所得到的数学上完全正确的结果。所有遵循同一标准的设备、编程语言和浏览器都会产生相同的答案。

为什么这个计算器显示 0.30000000000000004 而不是更简洁的结果
上方计算器使用 JavaScript 的 new Function() 来计算表达式,这意味着它直接依赖 JavaScript 引擎原生的 64 位浮点运算,没有任何隐藏的舍入层来清理输出结果。
在内部,代码对大多数数字会将结果舍入到 10 位小数——即 Math.round(result * 1e10) / 1e10——但 0.30000000000000004 的第一个意外数字出现在第十七位小数处,远超该舍入阈值。因此,真实的浮点结果会原封不动地传递出来。
部分计算器会在显示前将结果激进地舍入到 12 或 13 位有效数字,以此来隐藏这一现象。这种做法会显示出更友好的 0.3,但会隐藏科学计算或财务计算实际可能需要的精度。这种权衡是真实存在的:显示舍入越多,意外输出越少,但可见的有效位数也越少。
这会影响每一个小数计算吗?
不会——这正是关键所在。误差是否出现,取决于结果能否在 64 位二进制中精确表示。某些加法恰好能完美舍入:
- 0.1 + 0.4 = 0.5 —— 0.5 在二进制中可以精确表示(以 2 为基数即
0.1),因此误差相互抵消,显示出整洁的结果。 - 0.25 + 0.25 = 0.5 —— 原因相同:两个值都是 2 的精确幂次。
- 0.1 + 0.2 = 0.30000000000000004 —— 0.1 和 0.2 都无法精确表示,它们的累积误差足够大,能够在显示舍入后依然保留。
这里的规律并不是"计算器的小数计算有误",而是更为精确的表述:计算器对二进制小数的计算是精确的,而大多数日常小数并不是二进制小数。
当精度至关重要时该怎么办
- 日常算术 —— 误差出现在第十六或第十七位小数处,对于购物、烹饪或记账来说完全无关紧要。
- 财务计算 —— 尽可能以整数分(而非小数元)为单位进行运算,或使用专门的财务软件,这类软件采用定点运算。
- 工程或科学计算 —— 注意精度限制(IEEE 754 双精度约为 15–17 位有效数字),并判断其是否在你的测量误差范围内。
- 验证某个疑虑 —— 用预期值做减法:在上方计算器中输入
0.1 + 0.2 - 0.3,结果约为5.55e-17,即误差的实际大小。
动手试试:在上方计算器中输入0.1 + 0.2 - 0.3。结果不是零——而是约5.55e-17,即存储近似值与真实值之间的精确差距。这个微小的数字,正是 0.30000000000000004 中多出的4所代表的含义。