expm1“函数避免了直接评估小 x 的 exp(x)-1 所涉及的精度损失。”
关于什么是小,是否有经验法则? 1e-1, 1e-10, 1e-100 ?
(边问:是否有性能或其他原因不总是使用 expm1?)
np.info(np.expm1)
Examples
--------
The true value of ``exp(1e-10) - 1`` is ``1.00000000005e-10`` to
about 32 significant digits. This example shows the superiority of
expm1 in this case.
>>> np.expm1(1e-10)
1.00000000005e-10
>>> np.exp(1e-10) - 1
1.000000082740371e-10
最佳答案
简答
如果 6 个有效十进制数字对您来说足够了,那么 exp(x)-1
就可以了,直到 x 为 1e-10。通常,当 x 大约为 10**(-N)
时,您将失去 N 个十进制数字的精度。我们从大约 16 位 double 数字开始。
为什么不总是使用 expm1
?因为 expm1(x) + 1
绝对没有 exp(x)
的优势,无论 x
有多小。只有当您的计算实际上需要类似 expm1
的东西时,才使用 exp(x) - 1
才有意义。人们必须考虑计算的更广泛的背景。
长答案
这并不是关于 x 有多小,而是关于如何在计算中使用 exp(x)
。 expm1
的目的应该在更广泛的 Loss of significance 上下文中理解。某些公式在某些参数范围内会失去显着性;人们必须分析公式,看看它是否发生以及何时发生。如果在某个范围内存在潜在的显着性损失,请将公式改写为代数等效但数值稳定的公式。维基百科在二次方程的例子中很好地解释了这一点。
如果您的目标是计算 exp(x)
或 3*exp(x) + 4
等,则应使用 exp
。无论 expm1
有多小,这里都不会失去意义,将 x
放入这样的公式中也没有任何好处。编写 expm1(x) + 1
而不是 exp(x)
完全没有意义。
如果您的公式是 exp(x) - 1
或 exp(x) - cos(x)
,那么小 x
可能会失去意义。这并不总是重写的理由;如果您只打算在 x 为 1 或更大时使用此公式,则没有问题。如果您对机器 epsilon 级别(1e-16 左右)的绝对误差没问题,并且不太关心相对误差,则没有问题。
当发生显着性损失时,由信息的最终用户决定可以接受多少损失。通常,获得 6 个有效数字对于实际用途来说已经足够了,因此在 double 精度中丢失 10 个十进制数字可能是可以接受的。在这种情况下,当 exp(x) - 1
小于 x
时,公式 1e-10
会导致无法接受的精度损失。确实,exp(x) - 1
的值接近 x
,但 exp(x)
看起来像 1.00000000... 点后 10 位为 0;所以 x 本身只剩下 6 位数字。
以更安全的数值形式重写函数需要一些人力,以计算出所需的任何代数或三角恒等式。这种重写的例子:
f = lambda x: np.exp(x) - np.cos(x)
g = lambda x: np.sqrt(x**2 + 1) - 1
以上数字更安全的形式:
f_safe = lambda x: np.expm1(x) + 2*np.sin(x/2)**2
g_safe = lambda x: x / (np.sqrt(x**2 + 1) + 1)
将
np.exp(x) - np.cos(x)
重写为 np.expmp(x) - np.cos(x) + 1
根本没有任何好处;人们必须仔细考虑整个计算以消除几乎相等的数字的减法。关于python - 何时使用 python 函数 expm1 而不是 exp-1,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/47725508/