Method for calculating precise logarithm of a sum and subtraction
Disclosed is a method to compute the precise value of the logarithm of a sum.
A number of practical problems can result in having too big or small values in intermediate values of a calculation. Then one tries to take logarithm of these values and operate on logarithms instead. For example this is often the case in Artificial Intelligence and Speech Recognition, for example when the numbers represent probabilities in Bayesian Networks.
Also because multiplication and division is sometimes computationally more expensive than addition and subtraction, it is interesting to consider the log-values of x instead of x itself. In practice, for performance reasons also, integer arithmetic is sometimes used instead of floating-point arithmetic. Of course these two additional performance considerations apply only in the case when logarithm of summation and subtraction is done relatively more rarely during the calculations, so that other performance benefits overweight the losses.
In the case: log (p · q) = log p + log q is very easy to compute, but the problem is then to compute (or approximate) the value of log (p1 + p2) from the value of log p1 and log p2.
Let us assume that log a and log b are known, and that we want to approximate log (a + b).
Most basic solution would be calculating
sum_log = log(exp(a_log) + exp(b_log),
where a_log = log a and b_log = log b and therefore sum_log = log(a + b).
But the method I propose requires calling only one exp() and one log(), instead of two exp() and one log() in the basic solution. Additionally, the proposed method has the critical advantage of not overflowing in case of large numbers of a and b.
The method is based on the notion that
ln(a + b) = ln{exp[ln(a) - ln(b)] + 1} + ln(b)
Method's code in C++:
float add_lns(float a_ln, float b_ln)
{ if (abs(a_ln - b_ln) >= 15.942385) //2^23-1 = 8388607. log of that is 15.942385033669445460387166503854 { returnmax(a_ln, b_ln); //this branch is necessary, to avoid shifted_a_ln = a_ln - b_ln having too big value } else { float shifted_a_ln = a_ln - b_ln; float shifted_sum = exp(shifted_a_ln) + 1; float shifted_sum_ln = log(shifted_sum); float unshifted_sum_ln = shifted_sum_ln + b_ln; return unshifted_sum_ln; } }
Corresponding Matlab code:
function R = add_lns(a_ln, b_ln) % ln(a + b) = ln{exp[ln(a) - ln(b)] + 1} + ln(b)
if (abs(a_ln - b_ln) >= 36.043653389117155) % 2^52-1 = 4503599627370495. log of that is 36.043653389117155867651465390794 R = max(a_ln, b_ln); % this branch is even necessary, to avoid shifted_a_ln = a_ln - b_ln having too big value else shifted_a_ln = a_ln - b_ln; shifted_sum = exp(shifted_a_ln) + 1; shifted_sum_ln = log(shifted_sum); unshifted_sum_ln = shifted_sum_ln + b_ln; R = unshifted_sum_ln; end
end
Comments.
1) Explanation for comparisons"if (abs(a_ln - b_ln) >= 16)" and "if (abs(a_ln - b_ln) >= 36.043653389117155)":
2^23-1 is the maximal value (precision) of mantissa of 32-bit single-precision floating point value in Math Processor. Analogously 2^52-1 is the maximal value (precision) of mantissa of 64-bit double-precision floating point (the default floating point data type in Matlab). So it is meaningless to add numbers different by this order of magnitude - the bits of the resulting value will depend only on, that is - equal to - the original bits of the largest of the two values. Moreover, this would not only be meaningless - it would also make the method vulnerable to big numbers and overflows caused by them, which would nullify the point of the method.
In case one is using double-precision floats one likely wants to adjust this constant to an appropriate value.
2) Another way to look at the method is not to think about the proof
ln(a + b) = ln{exp[ln(a) - ln(b)] + 1} + ln(b)
but instead to think about it as scaling the value of a down to the "units" of b and replacing the value of b with 1 accordingly. That is a' = a / b and b' = 1 (assuming without the loss of generality that a > b). Then the scaled down (or scaled up) numbers are added and logarithm is taken of the resulting sum. Finally, the result is scaled back to original units, by adding the log b. That is equivalent to the formula:
Because the summation is done while the numbers are "scaled down" the overflows will not occur.
Calculating logarithm of subtraction
The method is based on the notion that ln(a - b) = ln{exp[ln(a) - ln(b)] - 1} + ln(b), if a > b. Note that we cannot compute the logarithm when a ≤ b. The principle is analogous to summation’s principle: Given that a' = a / b and b' = 1 we can represent the scaled down (or scaled up) values as a - b = (a / b - 1) · b = (a' - b') · b → ln(a - b) = ln(a / b - 1) + ln(b) = ln{exp[ln(a / b)] - 1} + ln(b) = = ln{exp[ln(a) - ln(b)] - 1} + ln(b).
Method's code in C++:
float sub_lns(float a_ln, float b_ln) { if (a_ln - b_ln >= 15.942385) //2^23-1 = 8388607. log of that is 15.942385033669445460387166503854 { return a_ln; //this branch is necessary, to avoid shifted_a_ln = a_ln - b_ln having too big value } else { float shifted_a_ln = a_ln - b_ln; float shifted_diff = exp(shifted_a_ln) - 1; float shifted_diff_ln = log(shifted_diff); float unshifted_diff_ln = shifted_diff_ln + b_ln; return unshifted_diff_ln; } }
Corresponding Matlab code:
function R = sub_lns(a_ln, b_ln) % ln(a - b) = ln{exp[ln(a) - ln(b)] - 1} + ln(b) if (a_ln - b_ln >= 36.043653389117155) % 2^52-1 = 4503599627370495. log of that is 36.043653389117155867651465390794 R = a_ln; % this branch is necessary, to avoid shifted_a_ln = a_ln - b_ln having too big value else shifted_a_ln = a_ln - b_ln; shifted_diff = exp(shifted_a_ln) - 1; shifted_diff_ln = log(shifted_diff); unshifted_diff_ln = shifted_diff_ln + b_ln; R = unshifted_diff_ln; end end
* http://roland.pri.ee/wiki/fast_log_function - a code snippet to replace the slow log() function... It just performs an approximation, but the maximum error is below 0.007.
The last thing we needed was another bout of you denied it but you were caught on tape histrionics, [url=http://www.replicalouisvuittonhandbags2u.com]replica louis vuitton handbags[/url]. Deja vu seems to be one of the traits of Irish politics, and what we get is largely to be expected, [url=http://www.replicalouisvuittonhandbags2u.com]louis vuitton replica[/url].
louis vuitton replica business as usual. Enda Kenny did indeed break an election party promise (and a personal one, no matter how he puts it), and [url=http://www.replicalouisvuittonhandbags2u.com]replica lv bags[/url], as a result, rather than having a meaningful debate on whether or not these replica lv bags should be closed, we have a bout of handbags over who said what.
We also had the louis vuitton bags showing of hypocrisy that has recently enveloped Fianna Fail. Has anyone spotted that Micheal Martin has been breaking his own post-election promise of constructive opposition? Maybe someone should dig up the audio tape on that one [url=http://www.replicalouisvuittonhandbags2u.com]louis vuitton bags[/url], because for the past three months he has provided replica louis vuitton handbags.