如何计算exp(-1 / x ^ 2)?

时间:2013-12-15 11:51:09

标签: c numeric

x非常接近于零时,我正在寻找一种精确计算的简洁方法:

exp(-1/x^2)

最好的方法是什么(速度,精度等)?

2 个答案:

答案 0 :(得分:1)

要保持极值x的精度,请分别处理x计算的整数和小数部分。

// exp(-1/x^2)
void expm1overxx(double x, double *PowerOf10, double *FractionPowerOf10) {
  // Handle cases where x is not very close to zero
  // This path is not important for this answer, but here as a stub for later work.
  // For now, simple continue
  if (fabs(x) >= 1.0) {
    ;
  }
  static const double OneOverLn10 = 0.43429448190325182765112891891661;
  double y = -OneOverLn10/x/x;
  *PowerOf10 = floor(y);
  *FractionPowerOf10 = y - *PowerOf10;
}

void printf_expm1overxx(double x) {
  double PowerOf10, FractionPowerOf10;
  expm1overxx(x, &PowerOf10, &FractionPowerOf10);
  char buf[20];
  sprintf(buf, "%+0.15f", pow(10.0, FractionPowerOf10));
  printf("f(%10f) = %se%-8.0f. = %.15e\n", x, buf, PowerOf10, exp(-1/x/x));
}

void test_expm1overxx(void) {
  printf_expm1overxx(0.01);
  printf_expm1overxx(0.02);
  printf_expm1overxx(0.03);
  printf_expm1overxx(0.04);
  printf_expm1overxx(0.05);
  printf_expm1overxx(0.1);
  printf_expm1overxx(0.2);
  printf_expm1overxx(0.3);
  printf_expm1overxx(10);
}

       x             f(x)                     f(x) using exp(-1/x^2)
f(  0.010000) = +1.135483865314536e-4343   . = 0.000000000000000e+00
f(  0.020000) = +1.835672669162076e-1086   . = 0.000000000000000e+00
f(  0.030000) = +2.822121211968184e-483    . = 0.000000000000000e+00
f(  0.040000) = +3.680855854801760e-272    . = 3.680855854801800e-272
f(  0.050000) = +1.915169596714143e-174    . = 1.915169596714115e-174
f(  0.100000) = +3.720075976020902e-44     . = 3.720075976020889e-44
f(  0.200000) = +1.388794386496408e-11     . = 1.388794386496407e-11
f(  0.300000) = +1.494533852478144e-5      . = 1.494533852478143e-05
f( 10.000000) = +9.900498337491680e-1      . = 9.900498337491681e-01

printf()中存在一些需要解决的组合问题,例如当非常pow(10, fraction)接近1.0时,但需要更详细的OP的意图和使用“精确计算”。

注意:假设xerr的比例错误为exp(x)err*exp(x)中的比例错误为-OneOverLn10/x/x。通过使用额外的精度,可以减轻误差和范围的可能性,但不能消除。可以对上面的y进行更精细的控制,但整体解决方案是以某种方式获得额外的精度。以上利用了{{1}}

的整数和小数部分

答案 1 :(得分:0)

最快:

        long double expatinfinity;

        /* can remove abs if x is strictly positive */
        if ( abs(x) < SENTINEL_VALUE)
           expatinfinity = 0;
        else
           expatinfinity = exp(-1.0/(x*x));

更精确(显式幂级数):

        long double expatinfinity;
        long double t = 1.0/(x*x); /* be mindful of type */
        int n = 0;
        long double term;

        if( abs(x) < SENTINEL_VALUE)
           while(1){
               term = pow(-t,n)/fact(n);
               if(term < THRESHOLD) break;
               expatinfinity += term;
               ++n;
           }
        else
          expatinfinity = exp(-1.0/x*x);

SENTINEL_VALUE是一个小值,用于确定计算所需的精度,并取决于实际应用(我猜一些高斯统计数据)THRESHOLD基本上是您想要容忍的错误关于泰勒扩张。它还取决于您使用的变量类型和机器可提供的精度。它还取决于在各种平台上一致工作的能力。换句话说,它在很大程度上取决于应用程序。

另一种方法是仍然执行1 / x ^ 2 - &gt;改变变量,只取一阶项(1 - t)。或者从泰勒扩张中选择固定数量的术语。这种方法的缺点是不受控制的精度,但优点是恒定的时间评估,您可以将扩展项存储在一个数组中并重用它们。

fact是阶乘函数(N *(N -1)*(...)* 1)。不记得数学库是否提供它。如果没有,你将不得不自己写。如果你不使用math.h并且提供自己的实现,你将节省内存。