(C++11 variant of shuvro's code, using the Standard Library as per the question.)
template <typename Iter>
std::iterator_traits<Iter>::value_type
log_sum_exp(Iter begin, Iter end)
{
using VT = std::iterator_traits<Iter>::value_type{};
if (begin==end) return VT{};
using std::exp;
using std::log;
auto max_elem = *std::max_element(begin, end);
auto sum = std::accumulate(begin, end, VT{},
[max_elem](VT a, VT b) { return a + exp(b - max_elem); });
return max_elem + log(sum);
}
This version is more generic - it will work on any type of value, in any type of container, as long as it has the relevant operators. In particular, it will use std::exp
and std::log
unless the value type has its own overloads.
To be really robust against underflow, even for unknown numeric types, it probably would be beneficial to sort the values. If you sort the inputs, the very first value will be max_elem
, so the first term of sum
will be exp(VT{0}
which presumably is VT{1}
. That's clearly free from underflow.