【问题标题】:Solve quadratic equation when coefficients may be 0当系数可能为 0 时求解二次方程
【发布时间】:2018-09-04 21:46:18
【问题描述】:

我遇到了一个问题,要编写一个 C 程序来求解方程 ax2+bx+c=0,其中 a , bcdouble 类型的系数。任何系数都可以为零。在这个问题中,我不清楚如何处理 double 变量。

这是我的代码。至于现在,我知道我的程序无法区分两个根和无限多个根。它也没有检测到“线性方程情况”。我怎样才能让它检测到无限数量的解决方案?如果 b > 0,cmets 还建议我在判别式之前用减号计算根,然后使用 Viet 定理。我知道这是因为将两个数字相加总是更准确。我还想我应该对 b

#include <stdio.h>
#include <math.h>
#include <float.h>

int main() {
    double a, b, c, x1, x2;

    scanf("%lf", &a);
    scanf("%lf", &b);
    scanf("%lf", &c); // just reading variables 
    //ax^2+bx+c=0
    if ((b * b - 4 * a * c) < 0) {
        printf("no"); 
    } else {
        x1 = (-b + sqrt(b * b - 4 * a * c)) / (2 * a); //calculating roots 
        x2 = (-b - sqrt(b * b - 4 * a * c)) / (2 * a);
        if ((fabs((a * x1 * x1 + b * x1 + c)) < DBL_EPSILON) & (fabs((a * x2 * x2 + b * x2 + c)) < DBL_EPSILON)) { //plugging the roots in
            if (fabs((x1 - x2)) < DBL_EPSILON) { //checking if the roots are equal
                printf("%lf", &x1); // if they are equal, we print only one of them
            } else {
                printf("%lf", &x1); // if they are not equal, we print both.
                printf("\n %lf", &x2);
            }
        } else { // if there are no two valid roots
            if ((fabs((a * x1 * x1 + b * x1 + c)) < DBL_EPSILON)) // we try to find one root.
                printf("%lf", &x1);
            if (fabs((a * x2 * x2 + b * x2 + c)) < DBL_EPSILON)
                printf("%lf", &x2);
            if ((fabs((a * x1 * x1 + b * x1 + c)) > DBL_EPSILON) & (fabs((a * x2 * x2 + b * x2 + c)) > DBL_EPSILON)) // if both of the plugged roots don't satisfy the equation
                printf("no");
        }
    }
    return 0;
}

【问题讨论】:

    标签: c floating-point double


    【解决方案1】:

    当系数可能为0时求解二次方程

    如何让它检测无限数量的解决方案?

    a==0 &amp;&amp; b == 0 &amp;&amp; c == 0.
    在这段代码的任何地方都不需要DBL_EPSILON。另见@Eric Postpischil

    但是如果 b == 0 呢?

    if (b == 0) {  // y = a*x*x + c
      if (a) {
        double dd = -c/a;
        if (dd >= 0) {
          double d = sqrt(d);
          printf_roots("+/- roots", d,-d);
        } else {
          printf_roots("Complex roots", NAN, NAN);  // Note NAN may not exist
        }
      } else if (c) { // y = 0*x*x + c, c != 0
        printf_roots("No roots", NAN, NAN);
      } else { // y = 0*x + 0
        printf_roots("Infinite roots", -HUGE_VAL, HUGE_VAL);
      }
    

    或者我应该只在 b

    除非编码目标在b==0 时需要特殊输出,否则我只会在b==0 上进行矢量编码作为a==0 发生时的子测试。

    if (a==0) {
      if (b == 0) {
    

    二次方程与许多 FP 代码一样,很容易溢出并达到 0,这两种情况都会丢失所有精度。

    考虑下面的代码:不必要的减法可能会导致溢出或截断为 0,而第二个可能不会。它依赖于很多东西。

    if ((b * b - 4 * a * c) < 0)
    // 
    if (b * b < 4 * a * c) 
    

    此外,C 允许使用更广泛的数学进行各种计算。研究FLT_EVAL_METHOD。因此,为防止sqrt(value_less_than_0),代码应计算discriminate,然后测试将应用于sqrt(x) 的对象x

    //if ((b * b - 4 * a * c) < 0) {
    //    printf("no"); 
    //} else {
    //    x1 = (-b + sqrt(b * b - 4 * a * c))
    
    double discriminate = b * b - 4 * a * c;
    if (discriminate < 0) {
        printf("no"); 
    } else {
        double d = sqrt(discriminate);  
        x1 = (-b + d)
    

    至于“如果 b > 0,则在判别式之前用减号计算根,然后使用 Viet 定理”的想法,我建议提高保留精度,如下所示,它不会像有符号值那样减去。

        double d = sqrt(discriminate);
    
        // Note x1*x2 = c/a
        if (b < 0) {
          x2 = (-b + d)/(2*a);
          x1 = c/a/x2;
        } else {
          x1 = (-b - d)/(2*a);
          x2 = c/a/x1;
        } 
        printf_roots("2 roots", x1, x2);
    

    关于printf("%lf", &amp;x1); 的注释。您没有在启用所有警告的情况下进行编译。节省时间 - 启用它们。应该是printf("%lf", x1); 不是&amp;

    进一步的double浮点。对于 FP 代码开发,请使用"%e""%a""%g" 以完整查看重要信息。

    printf("%g\n", some_double);
    // or better
    printf("%.*e\n", DBL_DECIMAL_DIG -1, some_double);
    

    【讨论】:

    • printf("%lf", &amp;x1); 应该是printf("%f", x1); 不是&amp;,也不是l
    • @chqrlie 从 C99 开始,l 可以,并且“对后面的 a、A、e、E、f、F、g 或 G 转换说明符没有影响。”
    【解决方案2】:

    由于不允许被零除,你必须将问题分成4种情况:

    1. a != 0: 这是您在代码中处理的情况。

    2. a == 0 && b != 0 : 这是一个线性方程,解为 x = -c/b

    3. a == 0 && b == 0 && c != 0 : x 没有可能的值。

    4. 在最后一种情况下,a、b 和 c 等于 0:x 有无穷多个解。

    编辑:删除与 epsilon 的比较,因为它们似乎没用

    【讨论】:

    • DBL_EPSILON 比较是不合适的。 a, b, and c`可以与== 0比较为零;相比之下,没有“模糊性”。与浮点范围小端的值相比,DBL_EPSILON 巨大;与之相比,即使不存在错误,abc 的完美值也会产生错误的结果。
    • @AntoineMathys: x:=-b/2a 不是 Δ
    【解决方案3】:

    你的代码有一些问题:

    • 您应该检查scanf() 的返回值,以避免在无效输入时出现未定义的行为。
    • 您应该为中间结果使用局部变量以提高代码的可读性
    • 您的printf 语句不正确:您应该传递双变量的值而不是它们的地址:printf("%lf", &amp;x1); 应该是:

      printf("%f", x1);
      

    关于退化的情况,你应该在尝试解决二次方程之前测试它们。

    这是一个更正的版本:

    #include <stdio.h>
    #include <math.h>
    
    int main() {
        double a, b, c, delta, x1, x2;
    
        if (scanf("%lf%lf%lf", &a, &b, &c) != 3) {
            printf("invalid input\n");
            return 1;
        }
        if (a == 0) {
            // not a quadratic equation
            if (b != 0) {
                printf("one solution: %g\n", -c / b);
            } else {
                if (c != 0) {
                    printf("no solution\n");
                } else {
                    printf("all real values are solutions\n");
                }
            }
        } else {
            delta = b * b - 4 * a * c;
    
            if (delta < 0) {
                printf("no real solution\n");
            } else
            if (delta == 0) {
                printf("one double solution: %g\n", -b / (2 * a));
            } else {
                x1 = (-b + sqrt(delta)) / (2 * a);
                x2 = (-b - sqrt(delta)) / (2 * a);
                printf("two solutions: %g, %g\n", x1, x2);
            }
        }
        return 0;
    }
    

    【讨论】:

      猜你喜欢
      • 2013-03-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多