进行非常小的(或大的)指数计算

时间:2015-10-06 19:30:30

标签: c++ math 32-bit exponential

大多数32位机器的指数限制是

exp( +/-700 )

但我想做一个指数计算

res = exp( x ) / exp( d )

当x或d大于700时,我使用的事实是

exp( x + y ) = exp( x ) . exp( y )

所以我的计算将是

res = (exp( x - z ).exp(z)) / (exp( d - z ).exp(z))

res = exp( x - z ) / exp( d - z )

其中(x-z)< 700

但是这种方法在某些情况下是有缺陷的,例如x = 6000和d = 10000的情况 如果我们使用z = 5300然后

res = exp( 6000 - 5300 ) / exp( 10000 - 5300 )

res = exp( 700 ) / exp( 47000 )

但是32位机器上的exp(47000)= 0。

如果我替换z = 9300,那我就会产生相反的效果。

res = exp( -3300 ) / exp( 700 )

那么考虑到计算机的局限性,我怎么能解决上面的等式,(我认为应该返回一个32位有效数字)?

修改 这样做的原因是我正在使用公式

P( a ) = P(y1) * P(y2) * P(y3) ... P(yN)

为了防止溢出,我做了

a = log( P(y1) ) + log( P(y2) ) +  log (P(y3)) ... log( P(yN) )

b = log( P(z1) ) + log( P(z2) ) +  log (P(z3)) ... log( P(zN) )

...

z = log( P(zz1) ) + log( P(zz2) ) +  log (P(zz3)) ... log( P(zzN) )

得到我做的总数

total = a + b ... z

并计算我做的百分比

(exp(a) / exp( total ) ) * 100

但有可能" a"和/或"总计"大于700

我想问题可能是如何在不使用指数

的情况下计算百分比

3 个答案:

答案 0 :(得分:4)

如果计算中的某些中间步骤不是,那么答案应该是32位数并不重要。

对于超出int或long类型边界的数学,您可能需要开始使用GMP之类的东西。

https://gmplib.org/

答案 1 :(得分:2)

我假设您想要计算:

p = exp(a) / exp(b)

a^b/a^c == a^(b-c)开始,这会减少到

p = exp(a - b)

可以很容易地计算如果差异低于该临界指数。

如果不是,那么你的结果不能用像double之类的原始数据类型来表示(因为它非常大或非常小),你需要某种任意精度数字,可能是由某些图书馆提供的。

但是如果您只需要打印结果或以某种方式存储它,那么您可以轻松计算甚至非常大的数字:

为此,您更改为基数10(用于显示),因此计算等效指数(tExponent = log10(eExponent)),并将该值设置为std::numeric_limits::max_exponent10std::numeric_limits::min_exponent10之间的允许范围,将差异保存为比例因子。

目前,我只有一个quick and dirty live example,显示

exp(90000) / exp(100)  =  1.18556 scaled by 10^39043

Check at wolfram alpha

注意:我写这篇文章的时候已经很晚了。我把它留在这里是为了另一种选择"方法

现在,通常,有

a^b = [a^(b/c)]^c

从那以后

(a/b)^c = (a^c)/(b^c)

holds, too,我想这里最简单的方法是只要将两个指数分开,只要其中一个超过你的临界值,然后进行取幂,除以结果,最后使用前者的除数指数作为指数的指数:

double large_exp_quot(
    double eNum,
    double eDenom,
    unsigned int const critical = 200) {
  if (abs(eNum - eDenom) > critical) {
    throw out_of_range{"That won't work, resulting exponent is too large"};
  }
  unsigned int eDivisor = 1;
  while (abs(eNum) > critical or abs(eDenom) > critical) {
    eNum /= 2;
    eDenom /= 2;
    eDivisor *= 2;
  }
  return pow(exp(eNum) / exp(eDenom), eDivisor);
}

但是这只会起作用,如果计算结果实际上可以使用C ++原始数据类型存储,在本例中为double。您给出的示例...使用exponents 6000和10000 ...显然无法用double表示(它e^(-4000)因此非常小)

答案 2 :(得分:1)

数值不稳定的计算:exp(a)/ exp(b)
等效稳定计算:exp(a - b)

数值不稳定的计算:Π i = 1..n p i
等效稳定计算:exp(Σ i = 1..n log(p i ))