分析下面代码的运行结果

- 判别式
d:
- 理论值应为
1e14 - 2。
- 由于浮点数精度限制,
1e14 远大于 2,导致 2 被忽略,d 的计算结果为 9999999.9999999013,存在微小误差。
- 大根
x1:
- 计算为
(-b + d) / (2a),是两个大数相加,结果 10000000.0000000000 是正确的。
- 小根
x2:
- 计算为
(-b - d) / (2a),是两个几乎相等的大数相减,发生了灾难性抵消。
d 的微小误差被放大,导致结果 0.0000000997 与理论值 0.0000001 相比,存在显著错误。
核心问题:直接套用数学公式在计算机上计算时,会因为浮点数的精度限制导致数值不稳定,尤其是在计算小根时,大数相减会严重损失精度。
算法框图
开始
输入a, b, c, e
如果 |a| < e
如果 |b| < e
如果 |c| < e
输出无穷多根
否则
输出无根
否则
x = -c / b
输出一个根x
否则
d = b*b - 4*a*c
如果 d < -e
输出无实根
否则如果 |d| < e
x = -b / (2*a)
输出两个相等根x
否则
d_sqrt = sqrt(d)
如果 b >= 0
q = -0.5 * (b + d_sqrt)
else
q = -0.5 * (b - d_sqrt)
如果 |q| < e
x1 = -b / (2*a)
x2 = x1
否则
x1 = q / a
x2 = c / q
输出两个根x1和x2
结束
代码
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
int main() {
double a, b, c, e;
printf("a, b, c, e: ");
scanf("%lf %lf %lf %lf", &a, &b, &c, &e);
double M = fmax(fmax(fabs(a), fabs(b)), fabs(c));
double a1, b1, c1, e1, e2;
if (M > 1e100 || M < 1e-100) {
a1 = a / M;
b1 = b / M;
c1 = c / M;
e1 = e / M;
e2 = e / (M * M);
} else {
a1 = a;
b1 = b;
c1 = c;
e1 = e;
e2 = e;
}
if (fabs(a1) < e1) {
if (fabs(b1) < e1) {
if (fabs(c1) < e1) {
printf("无限根\n");
} else {
printf("无根\n");
}
} else {
double x = -c1 / b1;
printf("一个根: x = %.10e\n", x);
}
} else {
double d1 = b1 * b1 - 4 * a1 * c1;
if (d1 < -e2) {
printf("无实根\n");
} else if (fabs(d1) < e2) {
double x = -b1 / (2 * a1);
printf("两个相等的根: x1 = x2 = %.10e\n", x);
} else {
double d_sqrt = sqrt(d1);
double q;
if (b1 >= 0) {
q = -0.5 * (b1 + d_sqrt);
} else {
q = -0.5 * (b1 - d_sqrt);
}
double x1, x2;
if (fabs(q) < e1) {
x1 = -b1 / (2 * a1);
x2 = x1;
} else {
x1 = q / a1;
x2 = c1 / q;
}
printf("两个不同的根: x1 = %.10e, x2 = %.10e\n", x1, x2);
}
}
return 0;
}