当x
非常接近于零时,我正在寻找一种精确计算的简洁方法:
exp(-1/x^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的意图和使用“精确计算”。
注意:假设x
中err
的比例错误为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并且提供自己的实现,你将节省内存。