在 JavaScript 中只有一种数值类型,Number,不区分整数类型和浮点数类型,因此所有的数字都是浮点型。
同时,JavaScript 又采用 IEEE 745(IEEE 二进制浮点数算术标准)定义的 64 位浮点格式来表示数字,不是精确的十进制。这意味着,如果一个数字超过了 64 位存储,就会发生精度的损失。
这就会导致,在 JavaScript 中 0.1+0.2
不等于 0.3 ,而是输出 0.30000000000000004
的浮点运算结果。
这是因为,一个数字是以二进制的形式存储在计算机当中,是一个只有 0 和 1 的序列。而十进制的部分小数,换算成二进制,会变成无限循环小数。二进制就是这么令人陌生而充满魅力,“bit” 这个词就来自“二进制数”(binary)和“位数”(digit)的组合。数字通过二进制成为语言。
沃兹尼亚克八年级的时候,就懂得用二进制做计算器,只要用到 00 只晶体管、200 只二极管、200 只电阻,装在了 10 块电路板上,战胜马上毕业的高中生,赢得当地空军的什么比赛。在 [[戈特弗里德·莱布尼茨]] 用一篇论文设计出现代二进制,《论只使用符号 0 和 1 的二进制算术,兼论其用途及它赋予伏羲所使用的古老图形的意义》,他认为八卦就是二进制计数方法。
如果将 0.1 转化为二进制,采用乘 2 取整法,可得 0.0 0011 0011 0011 0011…… 同理, 0.2 用二进制表示则是,0.0011 0011 0011 0011……两者相加,同样是无限循环的小数。
而计算机的资源有限,二进制的 64 位浮点格式无法精确存储 0.1 和 0.2 。就像在十进制下,1/3 会变成无限循环小数 0.33333333333333333……无法以 64 位小数精确存储。
所以 JavaScript 中的 0.1+0.2
,实际上是这两个数字的二进制近似值相加。这就是为什么浮点运算会在计算过程中丢失精度,性质与数学运算有所不同。
JavaScript 早期设计思想之一,就是不报错。当我们以为计算机出错没报错的时候,它往往正在进行一板一眼的逻辑运算。