问题描述
0.1 + 0.2
// => 0.30000000000000004
0.2 + 0.2
// => 0.4
0.3 + 0.2
// => 0.5
我知道这与浮点数有关,但是这里到底发生了什么?
I understand it has to do with floating points but what exactly is happening here?
根据@Eric Postpischil的评论,这不是重复的内容:
As per @Eric Postpischil's comment, this isn't a duplicate:
推荐答案
在JavaScript中将Number值转换为字符串时,默认值为使用足够多的数字来唯一地区分Number
值.这意味着当数字显示为"0.1"时,并不意味着精确地是0.1,而是比其他任何Number值都更接近0.1,因此仅显示"0.1"将告诉您这是唯一的Number值,即0.1000000000000000055511151231257827021181583404541015625.我们可以用十六进制浮点数表示为0x1.999999999999ap-4. (p-4
意味着将前面的十六进制数字乘以−4的两倍,因此数学家会将其写为1.999999999999 •2 .)
When converting Number values to strings in JavaScript, the default is to use just enough digits to uniquely distinguish the Number
value. This means that when a number is displayed as "0.1", that does not mean it is exactly 0.1, just that it is closer to 0.1 than any other Number value is, so displaying just "0.1" tells you it is this unique Number value, which is 0.1000000000000000055511151231257827021181583404541015625. We can write this in hexadecimal floating-point notation as 0x1.999999999999ap-4. (The p-4
means to multiply the preceding hexadecimal numeral, by two the power of −4, so mathematicians would write it as 1.999999999999 • 2.)
以下是在源代码中写入0.1
,0.2
和0.3
时得到的值,它们被转换为JavaScript的数字格式:
Here are the values that result when you write 0.1
, 0.2
, and 0.3
in source code, and they are converted to JavaScript’s Number format:
- 0.1→0x1.999999999999ap-4 = 0.1000000000000000055511151231257827021181583404541015625.
- 0.2→0x1.999999999999ap-3 = 0.200000000000000011102230246251565404236316680908203125.
- .3→0x1.3333333333333p-2 = 0.299999999999999988897769753748434595763683319091796875.
当我们评估0.1 + 0.2
时,我们将添加0x1.999999999999ap-4和0x1.999999999999ap-3.要手动执行此操作,我们可以先将后者的有效乘数(分数部分)乘以2,然后从其指数中减去1,以产生0x3.3333333333334p-4. (您必须以十六进制形式执行此算术运算.A •2 = 14 ,因此最后一位是4,然后携带1.然后是9 •2 = 12 ,带进位的1变为13 .这将产生3位数字和1个进位.)现在我们有0x1. 999999999999ap-4和0x3.3333333333334p-4,我们可以添加它们.这将产生4.ccccccccccccepep-4.那是精确的数学结果,但是对于Number格式而言,它有太多位.有效位数只能有53位. 4位(100 )中有3位,后13位中的每位有4位,因此总共为55位.计算机必须删除2位并四舍五入结果.最后一位E 是1110 ,因此必须保留10位.这些位恰好是前一位的½,因此它是向上舍入或向下舍入之间的纽带.打破平局的规则说要四舍五入,因此最后一位是偶数,因此我们四舍五入以使11位变为100.E变为10 ,从而导致携带到下一位.结果是4.cccccccccccd0p-4,等于0.3000000000000000444089209850062616169452667236328125.
When we evaluate 0.1 + 0.2
, we are adding 0x1.999999999999ap-4 and 0x1.999999999999ap-3. To do that manually, we can first adjust latter by multiplying its significand (fraction part) by 2 and subtracting one from its exponent, producing 0x3.3333333333334p-4. (You have to do this arithmetic in hexadecimal. A • 2 = 14, so the last digit is 4, and the 1 is carried. Then 9 • 2 = 12, and the carried 1 makes it 13. That produces a 3 digit and a 1 carry.) Now we have 0x1.999999999999ap-4 and 0x3.3333333333334p-4, and we can add them. This produces 4.ccccccccccccep-4. That is the exact mathematical result, but it has too many bits for the Number format. We can only have 53 bits in the significand. There are 3 bits in the 4 (100) and 4 bits in each of the trailing 13 digits, so that is 55 bits total. The computer has to remove 2 bits and round the result. The last digit, E, is 1110, so the 10 bits have to go. These bits are exactly ½ of the previous bit, so it is a tie between rounding up or down. The rule for breaking ties says to round so the last bit is even, so we round up to make the 11 bits become 100. The E becomes 10, causing a carry to the next digit. The result is 4.cccccccccccd0p-4, which equals 0.3000000000000000444089209850062616169452667236328125.
现在我们可以看到为什么打印.1 + .2
时显示的是"0.30000000000000004"而不是"0.3".对于Number值0.299999999999999988897769753748434595763683319091796875,JavaScript显示"0.3",因为该Number比任何其他Number更接近0.3.小数点后第17位的数字与0.3的差约为1.1,而第17位的相加结果与0.3的约4.4差. .所以:
Now we can see why printing .1 + .2
shows "0.30000000000000004" instead of "0.3". For the Number value 0.299999999999999988897769753748434595763683319091796875, JavaScript shows "0.3", because that Number is closer to 0.3 than any other Number is. It differs from 0.3 by about 1.1 at the 17 digit after the decimal point, whereas the result of the addition we have differs from 0.3 by about 4.4 at the 17 digit. So:
- 源代码
0.3
生成0.299999999999999988888869769753748434595763683319091796875并打印为"0.3". - 源代码
0.1 + 0.2
产生0.3000000000000000444089209850062616169452667236328125并显示为"0.30000000000000004".
- The source code
0.3
produces 0.299999999999999988897769753748434595763683319091796875 and is printed as "0.3". - The source code
0.1 + 0.2
produces 0.3000000000000000444089209850062616169452667236328125 and is printed as "0.30000000000000004".
现在考虑0.2 + 0.2
.其结果是0.40000000000000002220446049250313080847263336181640625.那是最接近0.4的数字,因此JavaScript将其打印为"0.4".
Now consider 0.2 + 0.2
. The result of this is 0.40000000000000002220446049250313080847263336181640625. That is the Number closest to 0.4, so JavaScript prints it as "0.4".
最后,考虑0.3 + 0.2
.我们将添加0x1.999999999999ap-3和0x1.3333333333333333p-2.再次,我们调整第二个操作数,生成0x2.6666666666666666p-3.然后相加产生0x4.0000000000000p-3,即0x1p-1,即1/2或0.5.因此将其打印为"0.5".
Finally, consider 0.3 + 0.2
. We are adding 0x1.999999999999ap-3 and 0x1.3333333333333p-2. Again we adjust the second operand, producing 0x2.6666666666666p-3. Then adding produces 0x4.0000000000000p-3, which is 0x1p-1, which is ½ or 0.5. So it is printed as "0.5".
另一种查看方式:
- 源代码
0.1
和0.2
的值分别略高于0.1和0.2,将它们相加会产生大于0.3的数字,并且误差得到了加强,因此总误差足够大,可以推动结果离0.3足够远,JavaScript会显示错误. - 添加
0.2 + 0.2
时,错误再次得到加强.但是,这种情况下的总错误不足以将结果推离0.4太远,以至于JavaScript用不同的方式显示结果. - 源代码
0.3
的值略低于0.3.当添加到0.2
(略大于0.2)时,错误将被抵消,精确地为0.5.
- The values for source code
0.1
and0.2
are both a little above 0.1 and 0.2, respectively, and adding them produced a number above 0.3, with errors that reinforced, so the total error was big enough to push the result away from 0.3 enough that JavaScript showed the error. - When adding
0.2 + 0.2
, the errors again reinforce. However, the total error in this case is not enough to push the result too far away from 0.4 that JavaScript displays it differently. - The value for source code
0.3
is a little under 0.3. When added to0.2
, which is a little over 0.2, the errors canceled, yielding exactly 0.5.
此规则来自ECMAScript 2017语言规范的7.1.12.1节的步骤5.
This rules comes from step 5 in clause 7.1.12.1 of the ECMAScript 2017 Language Specification.
这篇关于为什么0.1 + 0.2在JavaScript中返回不可预测的float结果,而0.2 + 0.3却没有?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!