[英]What are the benefits of the following C code?
给定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;
}
我想知道上述功能有什么好处?
主要(蛮力)替代方案如下:
v = log(exp(log_a) + exp(log_b));
这有三个超越功能评估。
显示的计算仅使用两个超越函数 - 并且应该更快。
它在数值上也可能更稳定。
计算机和日志并不总是相处得很好。 正如其他人所提到的那样,准确性成为一个真正的问题。 这篇博客文章对解释这一现象大有帮助。 这篇文章是关于看似不必要的库函数,以及为什么它们实际上非常方便。
函数log1p计算log(1 + x)。 这有多难实现?
在处理日志/指数时,您可以使用各种疯狂规则和变换。 我猜测的是,作者使用了其中一些规则来使计算更准确,更有效,或两者兼而有之。
其他人已经提到了潜在的精度损失,但在这种情况下问题确实是溢出 。 尝试这个:
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”。
如果你的意思是与log(exp(log_a) + exp(log_b))
,那么好处是非常清楚的; 你提到的方式只需要计算一个日志和一个exp,而这种方式必须计算两个exps。 这比额外的加/减/测试要昂贵得多。
不是一个答案,但可能是一个线索。
以“日志形式”保存的数字可以通过简单地加上或减去数字来相乘或除以。 例如, 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))
,但正如其他人所指出的那样,这种方式涉及三个昂贵的超越函数,而你的示例代码只需要两个(加上一个普通的比较)。
我想你问为什么这个函数比直接计算log(a+b)
更好,通过恢复a
和b
,求和它们并计算log()
:
log( exp( log_a ) + exp( log_b ) )
在这种情况下,您需要计算指数两次,并且在函数中,您询问指数只计算一次。 由于计算指数相对耗时,可能更快。
其他人已经发布了为什么要这样做的好答案。 我想知道if / else部分。 无论log_a
还是log_b
都更大, v
两个表达式都应该等于log(a+b)
。 在每种情况下, 0 < exp( ... ) <= 1
,并且log(1+exp( ... ))
是一个小的正数。 出于某种原因,我不知道这一定是好的。
如果你问的是if / else,那就是为了避免精度损失。 对浮点数的所有算术运算(乘以2的幂和除了相同指数的加法/减法的某些情况除外)都会破坏信息,良好的浮点代码将选择具有最小精度损失的方法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.