首先,我们看到以下代码:
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
。
那么,为什么加法的结果不同?
答案 0 :(得分:6)
它们在内存中的存储方式不同: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)
Sign再次为0,所以你有:
01000010110010000011001100110011
答案 1 :(得分:0)
除了Paul P.R.O的回答
这不仅仅是javascript的问题。在C#中,double表现出相同的行为
double a = 0.1, b = 0.2;
Console.WriteLine((a + b)==0.3); //Prints False
因此,建议在c#中使用decimal作为精度。对于javascript,您应该使用.toFixed来处理货币操作。有关javascript内部数字的详细信息,请查看http://www.hunlock.com/blogs/The_Complete_Javascript_Number_Reference#quickIDX0
答案 2 :(得分:0)