为什么这个方程的最大误差是4096*Number.EPSILON?
任何浮点值除以Number.EPSILON后大于1(或小于-1)的值必须是整数。(对于仅在1到-1范围内的值来说,这并不一定是正确的。)
回想一下,浮点数是位字符串(m,e) (尾数和指数)的两个元组,它们所代表的数字的值是m * 2^e。尾数将值设置为位字符串,指数在当前的ECMAScript 6草案中将比特移动到某个幂,Number.EPSILON的定义是:
1与大于1的最小值之间的差,可表示为数字值。
我们通过取1000...0001尾数的尾数(尾数中最大和最小数字的1)和将尾数向下移动到二进制值1.000...0001的负指数来得到epsilon。颠覆1,你就有了epsilon。请注意,这不是最小的浮点值,但它是浮点值可用于大于1(或小于-1).*的最小精度级别。
至于为什么你总是产生一个整数,这很容易解释: epsilon是数大于1的最小可能的精度值。epsilon不可能将大于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)中的最大输出位移位尾数。
代码语言:javascript运行复制1111111111111111111111111111111111111111111111111111100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000事实上,错误增加的唯一原因是由于尾数的固定大小,100...000.1111111111...中的尾数在减少。考虑:
代码语言:javascript运行复制> 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"请注意,这些字符串的长度完全相同,但小数点的左边在第二种情况下大一位数。这是因为尾数足够大,只能容纳一定数量的二进制数字。从逻辑上讲,应该是无限数量的1s变成只有在浮点数中合适的数量的1s。考虑类似的十进制情况,其中0.999……实际上等于1;因此,二进制0.11111...等于1,但是我们没有空间来表示无限位数。
由于尾迹的数目随着小数点左边的增加而减少,误差范围也随之增大,因为0.000...0001离小数点越来越近。
*:考虑像10001这样的简单尾数。如果您想使用尾数使值尽可能小,可以使用一个巨大的负指数来生成类似0.00000000000010001的值。但是,如果需要保持大于1的值(正如考虑Number.EPSILON定义时所做的那样),则只能将其向下转换为1.0001。最后的1可以走多远,这取决于尾数的大小,而前面的1必须停留在小数位的左边。如果你只是想创造最小的值,并且可以小于1,你可以把尾数移到右边更远的地方。