给定log(a)和log(b),返回log(a + b)
double log_sum(double log_a, double log_b){
double v;
if(log_a < log_b){
v=log_b+log(1+exp(log_a-log_b));
}
else{
v=log_a+log(1+exp(log_b-log_a));
}
return v;
}
我想知道上述功能有什么好处?
答案 0 :(得分:7)
主要(蛮力)替代方案如下:
v = log(exp(log_a) + exp(log_b));
这有三个先验函数评估。
显示的计算仅使用两个超越函数 - 并且应该更快。
它也可能在数字上更稳定。
答案 1 :(得分:5)
计算机和日志并不总是相处得很好。正如其他人所提到的那样,准确性成为一个真正的问题。 This blog post在解释这一现象方面走了很长的路。这篇文章是关于看似不必要的库函数,以及为什么它们实际上非常方便。
函数log1p计算log(1 + x)。这有多难实现?
在处理日志/指数时,您可以使用各种疯狂规则和变换。我猜测的是,作者使用了其中一些规则来使计算更准确,更有效,或两者兼而有之。
答案 2 :(得分:3)
其他人已经提到了潜在的精度损失,但在这种情况下,问题实际上是溢出。试试这个:
double log_a = 100;
double log_b = 1000;
printf("%f\n", log_b+log(1+exp(log_a-log_b)));
printf("%f\n", log_a+log(1+exp(log_b-log_a)));
在典型的平台上,第一个将打印“inf”,而第二个将打印“1000.000000”。
答案 3 :(得分:2)
如果你的意思与log(exp(log_a) + exp(log_b))
相反,那么好处就很明显了;你提到的方式只需要计算一个日志和一个exp,而这种方式必须计算两个exps。这比额外的加/减/测试要贵得多。
答案 4 :(得分:2)
不是一个真正的答案,而是一个可能的线索。
以“日志形式”保存的数字可以通过简单地加上或减去数字来相乘或除以。例如,exp(log(a) + log(b))
与a * b
相同。或者,使用a = 41,b = 101,这将是exp(3.71357 + 4.61512)
,即exp(8.32869)
或4140.98930
。显然精确度起作用,我将数字截断为5位数。 41 * 101
为4141
。
我没有完成你的示例代码,也没有立即明白为什么你的代码按照它的方式做事情,但希望上面的代码可以帮助你把它拼凑起来。
编辑:我在您的示例代码中运行了一些数字。如果a = 41且b = 101,log_a = 3.71357且log_b = 4.61512,则示例代码计算4.95582
,exp(4.95582)
等于142.0
。获得相同结果的“更简单”方法是log(exp(log_a) + exp(log_b))
,但正如其他人所指出的那样,这种方式涉及三个昂贵的超越函数,而您的示例代码只需要两个(加上一个简单的比较)。
答案 5 :(得分:0)
我想你问为什么这个函数比直接计算log(a+b)
更好,通过恢复a
和b
,将它们相加并计算log()
:
log( exp( log_a ) + exp( log_b ) )
在这种情况下,您需要计算指数两次,并且在函数中,您询问指数只计算一次。由于计算指数相对耗时,可能更快。
答案 6 :(得分:0)
其他人已经发布了为什么要这样做的好答案。我想知道if / else部分。无论log_a
或log_b
是否更大,v
的两个表达式都应该等同于log(a+b)
。在每种情况下,0 < exp( ... ) <= 1
和log(1+exp( ... ))
都是一个小的正数。出于某种原因,我不知道这一定是好的。
答案 7 :(得分:0)
如果你问的是if / else,那就是为了避免精度损失。对浮点数的所有算术运算(乘以2的幂和除了相同指数的加法/减法的某些情况除外)都会破坏信息,良好的浮点代码将选择精度损失最小的方法。