0

I just wanna know how I can optimize my C code. My program works fine, I tested it with many different values, all is good. However, I'd like to reduce the number of lines and write my program in better quality. Here's the source code:

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

int main(void) {
    float a,b,c,x,x1,x2;
    printf("aX^2 + bX + c = 0\n");
    printf("Type the value of a: ");
    scanf("%f", &a);
    printf("Type the value of b: ");
    scanf("%f", &b);
    printf("Type the value of c: ");
    scanf("%f", &c);

    if ( a!=0 && b!=0 && c!=0){
        float delta = b*b - 4*a*c;
        if (delta>0){
            x1 = (-b-sqrt(delta))/(2*a);
            x2 = (-b+sqrt(delta))/(2*a);
            printf("Solutions are x1 = %f and x2 = %f\n",x1,x2);
        }
        else if (delta == 0){
            x = -b/(2*a);
            printf("One unique solution is x = %f\n", x);
        }
        else {
            printf("No solutions !\n");
        }
    }
    if ( a==0 && b!=0 && c!=0)
        printf("One unique solution x = %f\n", -c/b);
    if ( a==0 && b==0 && c!=0)
        printf("No solutions !\n");
    if ( a==0 && b==0 && c==0 )
        printf("Set of solutions is R\n");
    if (a!=0 && b==0 & c!=0) {
        x = -c/a;
        if(x>=0)
            printf("Two soltions x = %f et -x = %f\n", sqrt(x),-sqrt(x));
        else{
            printf("No solutions !\n");
        }
    }
    if (a!=0 && b==0 && c==0)
        printf("One unique x = 0\n");

}
  • 3
    This question might be a better fit over at codereview.stackexchange.com . – Jim Lewis Nov 04 '16 at 21:51
  • @JimLewis I wasn't aware of this link, I'll put it there. Thanks mate. – Amine Marzouki Nov 04 '16 at 21:53
  • One small optimization: use `else if` instead of all raw `if`s. This will avoid unnecessary comparisons after one `if` condition is matched. – Code-Apprentice Nov 04 '16 at 21:57
  • @Code-Apprentice Isn't the same thing if I just replaced if with else if? Could you point out the unnecessary comparisons ? – Amine Marzouki Nov 04 '16 at 21:58
  • For example, say `a = 1`, `b = 2`, and `c = 1`. Then the first `if` will evaluate to `true` and its body will execute. Then the conditions of all the other `if`s will also be executed even though they are guaranteed to be false. On the other hand, if the `if`s are replaced with `else if`, then none of the following conditions will be executed at all. – Code-Apprentice Nov 04 '16 at 22:03
  • 1
    As a small technicality, you should probably change your message that says "No solutions" to "No real solutions" since there actually are complex solutions to these cases. – Code-Apprentice Nov 04 '16 at 22:09
  • 1
    Why use `float` instead of `double`? Why use `double sqrt(double)` instead of `float sqrtf(float)`? – chux - Reinstate Monica Nov 04 '16 at 22:12
  • @Code-Apprentice Aha got it. You're right. Thanks ! – Amine Marzouki Nov 04 '16 at 22:13
  • @chux float or double is the same thing, except that the float is 4 byte size while double is 8 byte. So Assuming that there would be no very big values I did it with float. – Amine Marzouki Nov 04 '16 at 22:17
  • @AmineMarzouki "no very big values I did it with float." --> then why use `double` function `sqrt()` rather than `sqrtf()`? Else code seems inconsistent sometimes using `double`, sometimes `float.`. In my experience little reason to code a quadric equation with `float`. OTOH, post better suited at codereview.stackexchange.com to well identity its various short-comings. – chux - Reinstate Monica Nov 04 '16 at 22:31
  • 1
    @chux Oh yes, You're right. Sorry ! I didn't notice sqrtf(), because I only knew the function sqrt(). I'll fix it. Thanks ! – Amine Marzouki Nov 04 '16 at 22:43
  • Your algorithm is well known to suffer from catastrophic cancellation leading to terrible precision. – EOF Nov 05 '16 at 07:27
  • @EOF Would you clarify please? – Amine Marzouki Nov 05 '16 at 10:23
  • @AmineMarzouki: This way of calculating the roots of a quadratic equation is one of the examples of algorithms containing catastrophic cancellation in the [oft-cited paper](http://download.oracle.com/docs/cd/E19957-01/806-3568/ncg_goldberg.html) on floating-point arithmetic. – EOF Nov 05 '16 at 20:46
  • @EOF I read the paper from the beginning to the Cancellation section, I understood most of what is there, and I saw the example. However, the example there just doesn't work, when I put the values in my program it gives me the true exact answer with no errors. Then I figured out what was happening, so my source code would have this issue of catastrophic cancellation if I fixed the number of floating points accuracy. eg: if you do printf("%.1f", 3.34*3.34); you'll get the answer like in the paper that is 11.2. But if you run the code without precision -> res: 11.1556 So maybe the problem is – Amine Marzouki Nov 06 '16 at 11:51
  • @EOF Maybe the problem is still there if by chance I had a multiplication with a big accuracy the it leads to the catastrophic cancellation. Although, it's unlikely to happen, I'll fix the accuracy to 10 to avoid this. Thanks a lot for your comment – Amine Marzouki Nov 06 '16 at 11:53
  • @AmineMarzouki The paper uses decimal floating-point numbers with three significant digits to illustrate that the problem is not specific to binary numbers, but rather to using representations with finitely many digits. The examples are not intended to show inaccuracy in 32-bit binary floating-point. – EOF Nov 07 '16 at 22:20

4 Answers4

1

One small optimization: use else if instead of all raw ifs. This will avoid unnecessary comparisons after one if condition is matched.

You can also experiment with the order of the if statements. If any conditions are more often to be true for your use cases, then they should be first.

Code-Apprentice
  • 81,660
  • 23
  • 145
  • 268
  • How would you expect the input that the user will write, in order to place some if statements first? – Amine Marzouki Nov 04 '16 at 22:01
  • @AmineMarzouki For example, since this is a quadratic equation solver, it seems highly unlikely that the user will enter `a` to be 0 since that case reduces to a linear equation. The `if` statements for these cases should probably be very last. – Code-Apprentice Nov 04 '16 at 22:07
1

Since you are basically building a state machine, let's actually do so:

int state = (a == 0) ? 0 : 1;
state |= (b == 0) ? 0 : 2;
state |= (c == 0) ? 0 : 4;

switch(state)
{
    case 0: // All co-efficents 0 Domain R print 
        break; 
    case 1: // Only a coefficient: 0 case
        break;
    case 2: // Only b coefficient 0 case.
        break;
    case 3: // a and b coefficient case - one root 0, other linear.
        break;
    case 4: // Only c case - no solutions
        break;
    case 5: // a and c case
        break;
    case 6:  // b and c case (linear)
        break;
    case 7: // a,b,c all here - do full quadratic solve.
         break;
    default:
       // This should never happen - assert and exit.
         break;
}

This way, all your cases are explicitly defined. Each case could also then call a function. Or, one step further, an array of function pointers for each case, fully and properly named.

Michael Dorgan
  • 12,453
  • 3
  • 31
  • 61
  • Your code sir, is a little bit complicated for me. I'm still a beginner with C. I didn't fully understand the syntax, but your idea is brilliant ! – Amine Marzouki Nov 04 '16 at 22:48
  • Basically, the `state` variable gets assigned a value from 0 to 7, depending on how many of your co-efficients are 0 or not zero (that is what the first 3 lines do.) Then, it takes that value and does 1 jump to get to where it needs to go. You could insert your prints at each comment and it would work great. – Michael Dorgan Nov 04 '16 at 23:25
  • But why did you write (a == 0) ? 0 : 1; while b with 0 and 2 and c with 0 and 4 ? I still don't get it – Amine Marzouki Nov 04 '16 at 23:31
  • 1,2,4 are decimal values for the bits in `state`. I set bit 0 (value 1) to true if a is non-zero. I set bit 1, (value 2) if b is non-zero. I set bit 2 (value 4) if c is non-zero. In this way, I have accounted for all possible permutations of a,b,c being zero or not zero. I am assigning the first time to make sure it is initialized, but then using binary-or |= for each additional one. – Michael Dorgan Nov 04 '16 at 23:36
1

1) Some of the parts of calculations are being repeated. Break the calculations into smaller pieces, store the results in variables, and use them to build your needed results.

2) You can avoid the special handling of the case a!=0 && b==0 & c!=0 entirely (albeit you need to change some of the other tests).

3) Some operations (e.g. prompting and reading) are repeated. Do these in a function which takes an argument (e.g. a string for prompt).

4) The first line where you are printing "No solutions" there are actually complex or imaginary solutions. The second place is another type of solution.

5) Possibly break some of the repeated calculations (point 1) into separate functions too.

6) Slightly advanced: your code will not deal well with a user who enters rubbish (non-numeric) input.

Peter
  • 35,646
  • 4
  • 32
  • 74
  • 1)That's the problem, I didn't know could I reduce the cases with ifs 2) any suggestions how to do that? 3) good point, but at the moment I'll not use them. 4) If you look in the title, I said R not C. So there would be no complex solutions in this program 5) same as 3) 6) I was always wondering about that, can you give me an example of a simple program that rejects non-numeric input? – Amine Marzouki Nov 04 '16 at 23:33
  • Well .... I'm telling you that it can be done. It's not difficult, but that's your job. You'll learn more by working it out for yourself than you will if I just rewrite your code for you. Just think through the mathematics of it. – Peter Nov 04 '16 at 23:36
0

You can use this example to optimise your code:

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

int main()
{
    double a, b, c, determinant, root1,root2, realPart, imaginaryPart;

    printf("Enter coefficients a, b and c: ");
    scanf("%lf %lf %lf",&a, &b, &c);

    if(a==0 && b!=0 && c!=0){
     root1 = -c/b;
     printf("linear equation root = -c/b = %.2lf", root1);
    }else{

    determinant = b*b-4*a*c;

    // condition for real and different roots
    if (determinant > 0)
    {
    // sqrt() function returns square root
        root1 = (-b+sqrt(determinant))/(2*a);
        root2 = (-b-sqrt(determinant))/(2*a);

        printf("root1 = %.2lf and root2 = %.2lf",root1 , root2);
    }

    //condition for real and equal roots
    else if (determinant == 0)
    {
        root1 = root2 = -b/(2*a);

        printf("root1 = root2 = %.2lf;", root1);
    }

    // if roots are not real 
    else
    {
        realPart = -b/(2*a);
        imaginaryPart = sqrt(-determinant)/(2*a);
        printf("root1 = %.2lf+%.2lfi and root2 = %.2f-%.2fi", realPart, imaginaryPart, realPart, imaginaryPart);
    }
}
    return 0;
}
  • If determinant is greater than 0, the roots are real and different.
  • If determinant is equal to 0, the roots are real and equal.
  • If determinant is less than 0, the roots are complex and different.

So,

Sample image

Esdras Suy
  • 39
  • 1
  • 7