首先,我们看下面的代码:
result = 0.1 + 0.2;
alert(result===0.3);// false
result=100.1+100.2;
alert(result===200.3);//true
我相信0.1
和100.1
(100+0.1) 类似地存储在内存中,因为小数点.1
在它们两者中。
那么,为什么加法的结果不同呢?
首先,我们看下面的代码:
result = 0.1 + 0.2;
alert(result===0.3);// false
result=100.1+100.2;
alert(result===200.3);//true
我相信0.1
和100.1
(100+0.1) 类似地存储在内存中,因为小数点.1
在它们两者中。
那么,为什么加法的结果不同呢?
它们在内存中的存储方式不同:http: //steve.hollasch.net/cgindex/coding/ieeefloat.html
0.1 二进制:
0.1 * 2 = 0 + 0.2
0.2 * 2 = 0 + 0.4
0.4 * 2 = 0 + 0.8
0.8 * 2 = 1 + 0.6
0.6 * 2 = 1 + 0.2
0.2 * 2 = 0 + 0.4
0.4 * 2 = 0 + 0.8
0.8 * 2 = 1 + 0.6
0.6 * 2 = 1 + 0.2
如您所见,在 0.2 重复后,整个模式 (0011) 将继续重复,0.1
因此0.0001100110011001100110011..
. 永远重复
为了在 23 位尾数中表示这一点,我们必须将其左移直到小数点前有 1(在将位左移 4 位并删除小数点前的 1 之后100110011...
),然后在第 23 位四舍五入,并且我们得到:10011001100110011001101
。
因为我们移动了 4 个位置,所以我们的指数是127-4
(127 是 32 位偏差)。8 位二进制中的 123 是01111011
,剩下的就是符号位,我们知道它是 0,因为 0.1 是一个正数。所以 32 位二进制数的每个分量是:
符号:0
指数:01111011
尾数:10011001100110011001101
0.1 表示为00111101110011001100110011001101
浮点数。
转换回来,我们必须将其分解,然后将指数转换回十进制整数 (123)。我们将尾数(前面假设为 1)向右移动(127 - 123 = 4)次并得到 :0.00011001100110011001101101
然后我们将其转换回十进制:
0*1/2 + 0*1/4 + 0*1/8 + 1/16 + 1/32 + 0*1/64 + 0*1/128 + 1/256 + 1/512 +
0 + 1/4096 + 1/8192 + 0 + 1/65536 + 1/131072 + 0 + 1/1048576 + 1/2097152 +
0 + 1/16777216 + 1/33554432 + 0 + 1/134217728
如果你做数学计算,你会得到更接近 0.100000001 的值,而不是 0.1。那是因为我们在第 23 位四舍五入。0.1 不能以二进制存储,因为它永远重复,所以在第 23 位之后我们有不准确之处。当你对一个数字进行算术运算时,这些不准确的地方就会出现,而且误差会变得更大。
如果你对 100.1 做同样的事情,你会得到:
1100100.0001100110011001100... 永远重复:
右移 6 次,去掉小数点前的 1 到第 23 位:10010000011001100110011
指数为 127+6 = 133 (1000 0101)
符号再次为 0,因此您有:
01000010110010000011001100110011
除了 Paul PRO 的回答
这不仅仅是 javascript 的问题。在 C# 中 double 表现出相同的行为
double a = 0.1, b = 0.2;
Console.WriteLine((a + b)==0.3); //Prints False
因此,建议在 c# 中使用小数来提高精度。对于 javascript,您应该使用 .toFixed 来处理货币操作。有关 javascript 中数字的更多信息,请查看http://www.hunlock.com/blogs/The_Complete_Javascript_Number_Reference#quickIDX0