在这个问题中,我编写了一个脚本来查找将值存储为总和的一小部分然后再返回时可能产生的最大错误.
我发现最大错误与Number.EPSILON有关:
maximumError / Number.EPSILON是一个很好的圆数,8192.
Math.log2(8192)是12.999999999999998,所以...... 13.
这个舍入错误与Number.EPSILON之间的关系是什么?
为什么它是2的好因素?13"是什么意思"?
更新:脚本刚刚发现最大误差为3.637978807091713e-12除以Number.EPSILON为16384. Math.log2(16384)〜= 14.
任何大于1(或小于-1)的浮点值除以的值Number.EPSILON 必须是整数.(这不是值一定是真的专门的范围内1来-1.)
回想一下,浮点是一个2元组的位串(m,e)(尾数和指数),其中它们代表的数字的值是m * 2^e.尾数设置值作为一个位串,且指数移位的位到一定的功率在当前的ECMAScript 6草案,定义Number.EPSILON为:
1和大于1的最小值之间的差值,可以表示为Number值
我们通过取尾数1000...0001(1在尾数位串中的最大和最小位数)和将尾数向下移动到二进制值的负指数来导出epsilon 1.000...0001.减去1,你有epsilon.请注意,这是不是可能的最小浮点值,但它是精确提供给浮点值大于1(或小于-1)的最小水平.*
至于为什么你总是产生一个整数,这很容易解释:epsilon是大于1的数字的最小可能精度值.epililon不可能不均匀地划分大于1的值,因为这表明数字有一些小于epsilon的小数部分,这在定义上是不可能的(因为epsillon是数字的最小精度级别>1).您可以随意在数字键盘上滚动并将该数字除以Number.EPSILON- 您将看到结果为整数.
至于为什么结果总是2的幂,这似乎是因为maximumError到目前为止你的所有结果的尾数也是2的幂(可能它们都有尾数1),所以它只是2的幂之间的除法(因此结果也必须是2)的幂.
请注意,所有的a产生这种情况的值是那些有1最低位,所以它们的形式是(2^n) + 1(和1自身): ,5,9,33,65等它似乎有在玩一些数学性质在这些地方乘法不会恢复完整值,原始100...001值变为100...000.1111111111....它的数量非常少,0.000000...000001.此数字的格式表示为1 * 2^-n,因此尾数始终为1.
您可能会发现num.toString(2)感兴趣的二进制表示:对于非常大或非常小的值,它清楚地显示二进制尾数偏移零,由指数的位移偏移引起* 2 ^ e.例如,请参见最大输出,位移尾数 Number.MAX_VALUE.toString(2):
1111111111111111111111111111111111111111111111111111100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Run Code Online (Sandbox Code Playgroud)
事实上,错误增长的唯一原因是由于尾数的固定大小,尾随1的数量100...000.1111111111...正在减少.考虑:
> var a = 9, b = 510; ((a/(a+b))*(a+b)).toString(2);
"1000.1111111111111111111111111111111111111111111111111"
> var a = 17, b = 4194; ((a/(a+b))*(a+b)).toString(2);
"10000.111111111111111111111111111111111111111111111111"
Run Code Online (Sandbox Code Playgroud)
请注意,这些字符串的长度完全相同,但在第二种情况下,小数点的左边是一个数字.那是因为尾数只有足够大才能容纳一定数量的二进制数字.从逻辑上讲,无限数量的1s应该变成1与浮点数一样多的s.考虑十进制的类似情况,其中0.999 ...实际上等于1 ; 因此,二进制0.11111...等于1,但我们没有空间来表示无限数字.
由于随着小数点左边的增加,拖尾数量减少,因此误差范围也会增加,因为0.000...0001越来越接近小数点.
*:考虑一个简单的尾数10001.如果你想使用那个尾数使值尽可能小,你可以使用一个非常负的指数来产生一个像这样的值0.00000000000010001.但是,如果您需要保持该值大于1(在考虑定义时Number.EPSILON),则只能将其向下移动1.0001.多远是最终1由尾数的大小,当领导是可以去界1必须留在小数位的左侧.如果您只是尝试创建最小值并且可以小于1,则可以将尾数向右移动得更远.