Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/57.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 如何计算exp(-1/x^2)?_C_Numeric - Fatal编程技术网

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

C 如何计算exp(-1/x^2)?,c,numeric,C,Numeric,当x非常接近于零时,我正在寻找一种干净的精确计算方法: exp(-1/x^2) 最佳方式(速度、精度等)是什么?最快: long double expatinfinity; /* can remove abs if x is strictly positive */ if ( abs(x) < SENTINEL_VALUE) expatinfinity = 0; else exp

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

exp(-1/x^2)
最佳方式(速度、精度等)是什么?

最快:

        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));
长双无限;
/*如果x严格为正值,则可以移除abs*/
if(绝对值(x)<前哨值)
无穷大=0;
其他的
无限大=exp(-1.0/(x*x));
更精确(明确的幂级数):

长双无限;
长双t=1.0/(x*x);/*注意字体*/
int n=0;
长期双轨制;
if(绝对值(x)<前哨值)
而(1){
术语=功率(-t,n)/事实(n);
如果(术语<阈值)中断;
无穷大+=项;
++n;
}
其他的
无限大=exp(-1.0/x*x);
SENTINEL_值
是一个很小的值,它决定了计算的精度,并取决于实际应用(我猜是一些高斯统计)
阈值
基本上是您希望在泰勒展开中容忍的错误。它还取决于您使用的变量类型和您的机器可以提供的精度。它还取决于跨各种平台一致工作的能力。换句话说,这在很大程度上取决于应用程序

另一种方法是仍然执行变量的1/x^2->t更改,并且只接受一阶项(1-t)。或者从泰勒展开式中选择固定数量的项。这种方法的缺点是精度不受控制,但优点是计算时间恒定,可以将展开项存储在数组中并重用它们


fact
是一个阶乘函数(N*(N-1)*(…)*1)。不记得数学库是否提供了它。如果不是,你就得自己写。如果不使用math.h并提供自己的实现,则可以节省内存。

要保持极限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()
中存在一些需要解决的组合问题,例如当非常接近1.0的
pow(10,分数)
时,但需要更详细地了解OP的意图和“精确计算”的用法


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

的整数部分和小数部分的优势。有多大?from的
exp()
from是否没有给出精确的结果?您希望结果有多精确
double
给出大约15位精度;这就足够了吗?@bitfidlingcodemonkey它从左到右求值,所以
-1./x/x
变成
(-1./x)/x
,等于-1/x²。@alk万分抱歉。让我们再试一次。exp()速度很快。指数级的快。x=0.01的值已经产生了一个难以置信的低值1E-4343,太小了,无法用双精度计算。错误在于只看术语,而不是术语出现的整个表达式。泰勒级数近似法应该会让人松一口气。谢谢塞巴斯蒂安。我不认为你的方法能处理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