我有一个简单的问题,对于谷歌来说真的很难(除了规范的What Every Computer Scientist Should Know About Floating-Point Arithmetic论文)。
log1p
什么时候应该使用or等函数expm1
来代替log
and exp
?什么时候不应该使用它们?这些功能的不同实现在使用方面有何不同?
我有一个简单的问题,对于谷歌来说真的很难(除了规范的What Every Computer Scientist Should Know About Floating-Point Arithmetic论文)。
log1p
什么时候应该使用or等函数expm1
来代替log
and exp
?什么时候不应该使用它们?这些功能的不同实现在使用方面有何不同?
我们都知道
意味着对于,我们有。这意味着如果我们必须在浮点中求值,对于可能会发生灾难性的取消。
这可以很容易地在 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
应该expm1
正确到不超过 1ULP(即最后一个单位)。但是,由于达到这种准确性会导致“慢”代码,因此有时可以使用快速但不太准确的实现。例如,在 CUDA 中,我们有expf
and expm1f
,其中f
代表快速。根据CUDA C 编程指南,app. D有expf
2ULP 的错误。
如果您不关心几个 ULPS 的错误,通常指数函数的不同实现是等效的,但要注意错误可能隐藏在某个地方......(还记得Pentium FDIV 错误吗?)
所以很明显expm1
应该用来计算小的。将它用于一般是无害的,因为预计在其整个范围内都是准确的:expm1
>>> exp(200)-1 == exp(200) == expm1(200)
True
(在上面的示例中,远低于的 1ULP ,因此所有三个表达式都返回完全相同的浮点数。)
类似的讨论适用于反函数log
,log1p
因为 for。