0

I'm working on a program that's supposed to calculate the roots of a quadratic function and output its roots. However, the output is not what it should be for all cases. When it's supposed to have no solutions or be trivial, it outputs as -nan(ind). When it's supposed to have one solution, it outputs x1 = -nan(ind) and x2 = -inf. I'm not too sure as to why this is happening and I could really use some help. Here is my code:

#include <iostream>
#include <cmath>
#include <iomanip>

using namespace std;

int main() {

    // Initialize and define the variables:
    // a = the variable that stores the value for 'a' in the quadratic
    // b = the variable that stores the value for 'b' in the quadratic
    // c = the variable that stores the value for 'c' in the quadratic
    // d = the variable that stores the determinant of the quadratic function to find the nature of the roots (b^2-4ac)
    // root1 = the variable that stores the first possible root of a quadratic function
    // root2 = the variable that stores the second possible root of a quadratic function
    // realNum = the variable that stores the real portion of the complex roots
    // imaginaryNum = the variable that stores the imaginary portion of the complex roots
    double a, b, c, d, root1, root2, realNum, imaginaryNum;

    // Ask the user to input a value for variable 'a' of the quadratic
    // NOTE: 'setprecision' specifies the minimum precision, 'fixed' states a fixed number of decimals will
    // appear after the entered digit
    cout << "Please input a: " << setprecision(4) << fixed;
    cin >> a;                                   /// Store the value in variable 'a'

    // Ask the user to input a value for variable 'b' of the quadratic
    // NOTE: 'setprecision' specifies the minimum precision, 'fixed' states a fixed number of decimals will
    // appear after the entered digit
    cout << "Please input b: " << setprecision(4) << fixed;;
    cin >> b;                                   /// Store the value in variable 'b'

    // Ask the user to input a value for variable 'c' of the quadratic
    // NOTE: 'setprecision' specifies the minimum precision, 'fixed' states a fixed number of decimals will
    // appear after the entered digit
    cout << "Please input c: " << setprecision(4) << fixed;;
    cin >> c;                                   /// Store the value in variable 'c'

    // Calculate the determinant of the quadratic (b^2 - 2ac)
    d = ((pow(b, 2.0)) - (4 * a * c));


    // Check to see if the determinant is greater than 0
    if (d >= 0) {

        // Calculate each of the two possible roots for the quadratic
        root1 = (-b + sqrt(d)) / (2 * a);
        root2 = (-b - sqrt(d)) / (2 * a);

        // Display to the user that a solution does exist for the following quadratic
        cout << "Your equation has real roots: " << root1 << " and " << root2 << "." << endl;

    }


    // Check to see if the determinant is greater than 0
    else if (d < 0) {

        // Calculate the real portion of the complex roots for the quadratic
        realNum = (-b) / (2 * a);

        // Calculate the imaginary portion of the complex roots for the quadratic
        imaginaryNum = (sqrt(-d)) / (2 * a);

        // Combine the two portions of the complex roots and display the calculated complex roots to the user
        cout << "Your equation has complex roots: " << realNum << " + " << imaginaryNum << "i and "
        << realNum << " - " << imaginaryNum << "i." << endl;

    }

    // Indicate that the program ended successfully
    return 0;

} // End of function main
Shreevardhan
  • 12,233
  • 3
  • 36
  • 50
  • nan means notANumber, are you dividing by Zero? – ΦXocę 웃 Пepeúpa ツ Feb 06 '17 at 09:07
  • 6
    Check values of a, b, c before proceeding. If `a` is zero then the equation in no longer quadratic. – sameerkn Feb 06 '17 at 09:09
  • 4
    Please provide testcases when it breaks (e.g. *exact* input and output). – Yuriy Ivaskevych Feb 06 '17 at 09:10
  • 1
    Select a degenerate test case. Write down expected values of all variables at each step of the execution. Stop and think if you see something unusual, otherwise fire up a debugger and check yourself. – n. m. could be an AI Feb 06 '17 at 09:42
  • `setprecision(4)` and `fixed` are sticky, you don't need to re-assert them each line – M.M Feb 06 '17 at 09:48
  • 1
    Contrary to other comments, dividing by zero gives an infinity in IEEE floating point, not a NaN. Calling `sqrt()` with a negative argument gives a NaN and (almost) any expression involving a NaN continues to give a NaN. So check if `d` is non-negative before computing `sqrt(d)` [this is the case where a quadratic equation has no real roots]. – Peter Feb 06 '17 at 11:02

3 Answers3

0

The problem is that you're dividing by 0 here:

 imaginaryNum = (sqrt(-d)) / (2 * a);  

The behavior of floats is defined by the IEEE-754 Standard. It states that if you divide a number by 0 the number will be represented as infinity. Furthermore, dividing by zero is undefined behaviour in C++.

That's why you want to check user input. You might not input invalid numbers, but the user could very well. This code will be a temporary workaround. Note that this is only a temporary solution to your problem, because strings like "12a" will still be accepted:

#include <iostream>
#include <exception>

int main() {
    double input;
    bool is_valid = false;
    while (is_valid != true) {
        try {
            cin >> input; //
            if (!cin) {
                cin.clear(); // reset failbit
                cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
                throw std::bad_exception();
            }
            else {
                is_valid = true;
            }
        }
        catch (...) {
            std::cout << "Input a number!\n";
        }
    }
    // ... Do this for all your inputs and proceed.
}
ScY
  • 91
  • 1
  • 7
0

Firstly, if a is tiny, we can say that the square term disappears. So it becomes a straight line with one root (unless parallel to the x axis).

Then if the determinant is negative, the equation does not cross the x axis. So there are no roots or, if you prefer, the roots are imaginary.

To improve numerical stability, use this function. It is written to avoid a near 0/0 term which can crop up using the highschool formula.

int quadratic_roots(double a, double b, double c, double *out)
{
    double rootb2minus4ac;
    double b2minus4ac;

    if(fabs(a) < FLT_EPSILON * 0.01)
    {
        if(fabs(b) > FLT_EPSILON * 0.01)
        {
            out[0] = c/-b;
            return 1;
        }
        return 0;
    }

    b2minus4ac = b*b - 4*a*c;
    if(b2minus4ac > 0)
    {
        rootb2minus4ac = sqrt(b*b - 4*a*c);
        if(b >= 0)
        {
            out[0] = (-b -rootb2minus4ac)/(2*a);
            out[1] = (2*c)/ (-b -rootb2minus4ac);
        }
        else
        {
            out[0] = (2*c)/(-b + rootb2minus4ac);
            out[1] = (-b + rootb2minus4ac)/(2*a);
        }
        if(a < 0)
        {
            double temp = out[0];
            out[0] = out[1];
            out[1] = temp;
        }
        return 2;
    }
    else if(b2minus4ac == 0.0)
    {
        out[0] = (2*c)/-b;
        return 1;
    }
    return 0;
}
Malcolm McLean
  • 6,258
  • 1
  • 17
  • 18
0

Here are the problematic parts of your code:

// Calculate each of the two possible roots for the quadratic
        root1 = (-b + sqrt(d)) / (2 * a);
        root2 = (-b - sqrt(d)) / (2 * a);
        //....
        // Calculate the real portion of the complex roots for the quadratic
        realNum = (-b) / (2 * a);

        // Calculate the imaginary portion of the complex roots for the quadratic
        imaginaryNum = (sqrt(-d)) / (2 * a);

As it can be clearly seen, the variable a is in the denominator. The variable a can have any value, including zero. Division by zero, can lead to Undefined behavior in C++. Also as pointed out by ScY:

The behavior of floats is defined by the IEEE-754 Standard. It states that if you divide a number by 0 the number will be represented as infinity.

My suggestion is to check whether a is equal with zero or not. If a is equal with zero, then throw an error.

cout << "Please input a: " << setprecision(4) << fixed;
cin >> a;     
if (a == 0){
   std::cerr << "The variable a should not be equal with 0";
   return 1;
}

I strongly suggest you, to create a function to calculate the roots of any given polynomial. This can make your code more readable and more efficient. Furthermore the main() function is not really that reusable, so it should be kept short.