e^x 没有 math.h
e^x without math.h
我试图在不使用 math.h 的情况下找到 ex。当 x 大于或小于 ~±20 时,我的代码会给出错误的答案。我试图将所有 double 类型更改为 long double 类型,但它在输入时产生了一些垃圾。
我的代码是:
#include <stdio.h>
double fabs1(double x) {
if(x >= 0){
return x;
} else {
return x*(-1);
}
}
double powerex(double x) {
double a = 1.0, e = a;
for (int n = 1; fabs1(a) > 0.001; ++n) {
a = a * x / n;
e += a;
}
return e;
}
int main(){
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
int n;
scanf("%d", &n);
for(int i = 0; i<n; i++) {
double number;
scanf("%lf", &number);
double e = powerex(number);
printf("%0.15g\n", e);
}
return 0;
}
输入:
8
0.0
1.0
-1.0
2.0
-2.0
100.0
-100.0
0.189376476361643
我的输出:
1
2.71825396825397
0.367857142857143
7.38899470899471
0.135379188712522
2.68811714181613e+043
-2.91375564689153e+025
1.20849374134639
右输出:
1
2.71828182845905
0.367879441171442
7.38905609893065
0.135335283236613
2.68811714181614e+43
3.72007597602084e-44
1.20849583696666
你可以看到我对e−100的回答完全不正确。为什么我的代码输出这个?我可以做些什么来改进这个算法?
当x
为负数时,每一项的符号交替。这意味着当使用正功率时,每个连续的总和的值变化很大,而不是逐渐增加。这意味着连续项的精度损失对结果有很大影响。
要处理这个问题,请在开始时检查 x
的符号。如果为负数,则切换x
的符号进行计算,然后在循环结束时反转结果。
此外,您可以使用以下违反直觉的条件来减少迭代次数:
e != e + a
从表面上看,这似乎总是正确的。但是,当 a
的值超出 e
的值精度时,条件变为假,在这种情况下,将 a
添加到 e
不会改变e
.
的值
double powerex(double x) {
double a = 1.0, e = a;
int invert = x<0;
x = fabs1(x);
for (int n = 1; e != e + a ; ++n) {
a = a * x / n;
e += a;
}
return invert ? 1/e : e;
}
我们可以通过用 0 而不是 a
初始化 e
并在循环底部而不是顶部计算下一项来进一步优化以删除一个循环迭代:
double powerex(double x) {
double a = 1.0, e = 0;
int invert = x<0;
x = fabs1(x);
for (int n = 1; e != e + a ; ++n) {
e += a;
a = a * x / n;
}
return invert ? 1/e : e;
}
对于x大于1的情况,可以考虑单独处理整数部分,通过平方计算e的幂。 (例如 e^9 = ((e²)²)².e 需要 4 次乘法)
果然是泰勒展开式的总称,x^n/n!仅在 n>x 后开始减少(每次乘以 x/k),因此求和至少需要 x 项。另一方面,e^n最多可以计算2lg(n)次,效率更高,更准确。
所以我建议
- 取x的小数部分并使用泰勒,
- 当整数部分为正时,乘以e的那个次方,
- 当整数部分为零时,你就完成了,
- 当整数部分为负数时,除以 e 的那个次方。
考虑四分之一,您甚至可以节省更多:在最坏的情况下 (x=1),泰勒需要 18 项才能使最后一项变得可以忽略不计。如果您考虑从 x 中减去 1/4 的紧邻倍数(并乘以 e 的预先计算的幂进行补偿),则项数将降至 12。
例如e^0.8 = e^(3/4+0.05) = 2.1170000166126747 。 e^0.05
我试图在不使用 math.h 的情况下找到 ex。当 x 大于或小于 ~±20 时,我的代码会给出错误的答案。我试图将所有 double 类型更改为 long double 类型,但它在输入时产生了一些垃圾。
我的代码是:
#include <stdio.h>
double fabs1(double x) {
if(x >= 0){
return x;
} else {
return x*(-1);
}
}
double powerex(double x) {
double a = 1.0, e = a;
for (int n = 1; fabs1(a) > 0.001; ++n) {
a = a * x / n;
e += a;
}
return e;
}
int main(){
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
int n;
scanf("%d", &n);
for(int i = 0; i<n; i++) {
double number;
scanf("%lf", &number);
double e = powerex(number);
printf("%0.15g\n", e);
}
return 0;
}
输入:
8
0.0
1.0
-1.0
2.0
-2.0
100.0
-100.0
0.189376476361643
我的输出:
1
2.71825396825397
0.367857142857143
7.38899470899471
0.135379188712522
2.68811714181613e+043
-2.91375564689153e+025
1.20849374134639
右输出:
1
2.71828182845905
0.367879441171442
7.38905609893065
0.135335283236613
2.68811714181614e+43
3.72007597602084e-44
1.20849583696666
你可以看到我对e−100的回答完全不正确。为什么我的代码输出这个?我可以做些什么来改进这个算法?
当x
为负数时,每一项的符号交替。这意味着当使用正功率时,每个连续的总和的值变化很大,而不是逐渐增加。这意味着连续项的精度损失对结果有很大影响。
要处理这个问题,请在开始时检查 x
的符号。如果为负数,则切换x
的符号进行计算,然后在循环结束时反转结果。
此外,您可以使用以下违反直觉的条件来减少迭代次数:
e != e + a
从表面上看,这似乎总是正确的。但是,当 a
的值超出 e
的值精度时,条件变为假,在这种情况下,将 a
添加到 e
不会改变e
.
double powerex(double x) {
double a = 1.0, e = a;
int invert = x<0;
x = fabs1(x);
for (int n = 1; e != e + a ; ++n) {
a = a * x / n;
e += a;
}
return invert ? 1/e : e;
}
我们可以通过用 0 而不是 a
初始化 e
并在循环底部而不是顶部计算下一项来进一步优化以删除一个循环迭代:
double powerex(double x) {
double a = 1.0, e = 0;
int invert = x<0;
x = fabs1(x);
for (int n = 1; e != e + a ; ++n) {
e += a;
a = a * x / n;
}
return invert ? 1/e : e;
}
对于x大于1的情况,可以考虑单独处理整数部分,通过平方计算e的幂。 (例如 e^9 = ((e²)²)².e 需要 4 次乘法)
果然是泰勒展开式的总称,x^n/n!仅在 n>x 后开始减少(每次乘以 x/k),因此求和至少需要 x 项。另一方面,e^n最多可以计算2lg(n)次,效率更高,更准确。
所以我建议
- 取x的小数部分并使用泰勒,
- 当整数部分为正时,乘以e的那个次方,
- 当整数部分为零时,你就完成了,
- 当整数部分为负数时,除以 e 的那个次方。
考虑四分之一,您甚至可以节省更多:在最坏的情况下 (x=1),泰勒需要 18 项才能使最后一项变得可以忽略不计。如果您考虑从 x 中减去 1/4 的紧邻倍数(并乘以 e 的预先计算的幂进行补偿),则项数将降至 12。
例如e^0.8 = e^(3/4+0.05) = 2.1170000166126747 。 e^0.05