什么时候应该使用 log1p 和 expm1?

计算科学 浮点
2021-12-20 20:05:50

我有一个简单的问题,对于谷歌来说真的很难(除了规范的What Every Computer Scientist Should Know About Floating-Point Arithmetic论文)。

log1p什么时候应该使用or等函数expm1来代替logand exp什么时候不应该使用它们?这些功能的不同实现在使用方面有何不同?

2个回答

我们都知道 意味着对于,我们有这意味着如果我们必须在浮点中求值,对于可能会发生灾难性的取消。

exp(x)=n=0xnn!=1+x+12x2+
|x|1exp(x)1+xexp(x)1|x|1

这可以很容易地在 python 中演示:

>>> from math import (exp, expm1)

>>> x = 1e-8
>>> exp(x) - 1
9.99999993922529e-09
>>> expm1(x)
1.0000000050000001e-08

>>> x = 1e-22
>>> exp(x) - 1
0.0
>>> expm1(x)
1e-22

精确值是

exp(108)1=0.000000010000000050000000166666667083333334166666668exp(1022)1=0.000000000000000000000100000000000000000000005000000

一般来说,“准确”的实现exp应该expm1正确到不超过 1ULP(即最后一个单位)。但是,由于达到这种准确性会导致“慢”代码,因此有时可以使用快速但不太准确的实现。例如,在 CUDA 中,我们有expfand expm1f,其中f代表快速。根据CUDA C 编程指南,app. Dexpf2ULP 的错误。

如果您不关心几个 ULPS 的错误,通常指数函数的不同实现是等效的,但要注意错误可能隐藏在某个地方......(还记得Pentium FDIV 错误吗?)

所以很明显expm1应该用来计算小的将它用于一般是无害的,因为预计在其整个范围内都是准确的:exp(x)1xxexpm1

>>> exp(200)-1 == exp(200) == expm1(200)
True

(在上面的示例中,远低于的 1ULP ,因此所有三个表达式都返回完全相同的浮点数。)1exp(200)

类似的讨论适用于反函数loglog1p因为 forlog(1+x)x|x|1

为了扩大 和 之间的差异,log如果log1p是对数,可能有助于回忆图表:

对数

如果您的数据包含零,那么您可能不想使用log,因为它未定义为零。并作为x方法0, 的价值ln(x)方法. 所以如果你的x值接近0,那么值ln(x)可能是一个很大的负数。例如ln(1e)=1ln(1e10)=10等等。这可能很有用,但它也会使您的数据向大的负数方向扭曲,尤其是当您的数据集还包含远大于零的数字时。

另一方面,如x方法0, 的价值ln(x+1)方法0从积极的方向。例如ln(1+1e)0.31ln(1+1e10)0.000045. 因此log1p只产生正值并消除大负数的“危险”。当数据集包含接近零的数字时,这通常可以确保更均匀的分布。

简而言之,如果数据集都大于1,那么log通常就可以了。但是,如果数据集的数字介于01, 那么log1p通常更好。