[英]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.