Roots of a Quadratic Equation

Overview

You are going to write a program which solves for the roots of a quadratic equation using the well know formula:

800

In this assignment you will do a number of things:

  • declare variables and assign values to them

  • do mathematical calculations using variables

  • declare methods

  • call methods

  • pass values to methods

  • return values from methods

  • read code and analyze it for errors

The completed assignment will consist of two things:

  1. A Java program to compute the roots

  2. Answers to several questions. The answers will be embedded as comments in your code. The questions are scattered through this page.

Understanding Methods

A method is a name given to one or more statements that performs some work on behalf of the code that calls it. To use it, the calling code simply uses its name() and the statements in the method are executed. Methods provide abstraction in the code, replacing the details of how the method does its work (the actual code) by a name which conveys what the method does. Choosing good names for variables and methods is important.

Methods are useful because they allow the writer to decompose a problem into smaller parts (the methods), then call them to complete the necessary work. Methods may be reused over and over again, simplifying the creation of more complex programs. Furthermore, one can call a method and defer the actual details of its implementation until later. One can concentrate on the big picture first, then take care of details later.

Methods may have parameter(s). For example the method Math.sqrt() takes a single parameter (a number), then calculates its square root. The user need not be concerned with how Math.sqrt() actually does its work. The user only cares what Math.sqrt() does.

Methods may return values (e.g. Math.sqrt()) or be void (e.g. System.out.println()). Methods that return values are "answering questions".

Part A - The base program

Getting Started

  1. Open your Eclipse program

  2. Create a new Project named P2 (File → new → Java Project)

  3. Create a new class named QuadEqn. (File → new → Class). Check the box that tells Eclipse to create public static void main(String[] args). Then press the Finish button. Eclipse will create a new class with an empty main() method.

  4. Put your name at the top of the file in a Java comment.

The main() method

In your main() method declare three double variables. You may give them any names you want (foo, bar, baz). BUT, as we are working on quadratic equations, it is probably better if you name them a, b, and c because that is the nomenclature used in the formula.

  1. Declare a double variable named a and assign it the value 6.0

  2. Declare a double variable named b and assign it the value 12.0

  3. Declare a double variable named c and assign it the value -48.0

  4. Declare a double variable named rad and assign it the value radical(a, b, c). What you are doing is calling a method and passing it three parameters. The result returned by the method is assigned to the variable.

Notice how the call to radical(a, b, c) is underlined in red. This is because Eclipse understands that you are making a method call, but it can not find any method named radical(). If you put the mouse over the underlined method name, Eclipse will explain the problem and offer you a quick fix. Take the suggested fix.

You are now ready to calculate and print the roots of the quadratic equation. Again, you are going to delegate the work to a method.

  1. Write code to call the method printRoot() passing the parameters a, b, and rad You should see that the method name printRoot() is underlined in red. Allow Eclipse to create an empty implementation for you.

  2. Write another line to call printRoot() a second time to print the other root. You should only need to add a single character to the code you wrote in the previous line.

At this point, you have a complete program, but the two methods have only skeleton implementations. This means that they will execute but will not do their intended job. The importance of this is that you can actually run your program and incrementally implement/test the methods one at a time.

The radical() method

You are now ready to implement radical(). The purpose of this method is to calculate the square root of the expression under the square root sign.

  1. Declare a double variable (select some name) and assign it to the square root of the expression shown in the formula. Note that the radical() method has three parameters passed in and you can simply use their names in the equation. To find the square root, use the method Math.sqrt(). You may use a single line of code or break it into multiple statements for the sub expressions.

  2. Add a System.out.println() and print the value you calculated.

  3. Modify the return statement to return the variable you created in the previous step(s) instead of 0.

Now test your program by running it. Your program should print the value radical() → 36.0. What you are doing is testing what you just completed. A best practice is to write a little code, test it until it is correct, and then repeat. At the end you will have a working program because you have tested each part along the way. Once you are certain your method works, you may comment out or remove the System.out.println().

The printRoot() method

This routine will calculate a single root and print it.

  1. declare a double variable and assign it to the root obtained by the addition in the formula.

  2. Add a System.out.println() to print the root prefaced by the string `Root: `.

Now run your program again. You should see Root: 2.0 and Root: -4.0.

You are DONE! However, your program only calculates the roots of a single quadratic equation. To calculate the roots of a different equation, you must modify the program and rerun it. Not very friendly for the user. So, lets fix that.

Generalizing QuadEqn

Did you notice that the main() method has parameters? We can easily take advantage of this to calculate the roots of any quadratic equation. We can pass the coefficients of the quadratic equation to main() so it is more useful. The declaration of main() is

  public static void main(String[] args)

The notation String[] means that the variable args is an array. An array is a type of variable that hold multiple values. Individual values in the array are accessed using an integer index. For example, if one considers a list of planets ordered by their distance from the sun, Mercury is the 1st and Earth is the 3rd planet. In computer arrays, the index starts at 0 rather than the 1 that humans typically use to denote the 1st element in a list. The syntax for accessing an individual value is variable[index] where index can be a number (e.g. 0), a variable (e.g. i), or even an expression (e.g. i+1).

Since values are passed into main(), how do we actually use them in the method?

  1. Replace the value assigned to a by args[0].

  2. What happens when we do this?

  3. Can we use any of the suggested quick fixes? Why not?

The problem is that a is a double, but args[0] is a String. So, you need to convert the string containing the characters 6 . 0 into the number 6.0. Fortunately, Java has a class Double (capital D intensional) that can help us. Find a method in the documentation that takes a String as an argument and return a double. Use the method you found and call it passing the parameter args[0]. Since this method is a static method in a different class, you need to preface the call with the class name (e.g. Double.methodName(args[0]).

Now fix the assignments for variables b and c.

Passing values to main()

To pass values to the main() method in Eclipse do the following:

  1. Bring up the configuration menu by selecting run → Run configurations…​.

  2. Select the tab labeled Arguments

  3. In the box labeled Program Arguments type in numeric values for the three coefficients a, b, and c.

Final steps

Run your program multiple times with different parameters. There are at least three situations that will produce unexpected results. Some may result in Exception. Others may produce strange output.

Now add an Answers section to your code as a Java block comment.

  1. As Java comments prior to the line public class QuadEqn, describe three ways to create problems for you program. If following your description causes an Exception, give the exact name of the exception.

  /* Answers
   1. If the users does ... this results in ...
   2.
   3.
  */

Checking in Your Code

Use the Checkin tab on the class web site and turn in the file QuadEqn.java.

PART B - Prompting the user for input

In Part B, you will modify your code to allow the user to enter the coefficients from the keyboard. Your code will prompt the user to enter a value and then when the user enters a value (followed by the Enter key), the value will be assigned to a variable.

Using a Scanner for keyboard input

In order to read from the keyboard, you must use the methods of an object designed to do just that. One way is to use a class called Scanner. Add the following line as the first line of main().

  Scanner kbd = new Scanner(System.in);

This statement is declaring a variable of type Scanner, named kbd. It is then initializing it by creating a new one and initializing this using the value System.in. One we have created kbd we can use it to access things that the user typed in. You can create a Scanner to read the contents of a text file or to read other sources of information. In Java the variable System.in represents the keyboard. It is used for input. System.out is used for output to the screen. You used this when you wrote System.out.println(). The method println() is a method of the variable System.in.

Using a method to collect input

So now you need to use the Scanner to actually get values from the users. This is a good place for a method. Add the following line to main() just after your declaration of kbd. What you are doing is saying that args will be initialized by reading from the keyboard, rather that externally.

   args = getCoefficients(kbd);

Again you will see that getCoefficients() is underlined in red. As you did earlier, place the mouse over the underlined code, and see what quick fix is available. Allow Eclipse to create the method for you.

Implementing getCoefficients()

Notice that getCoefficients() is responsible for returning an array of String. We can tell that by the [] notation. Furthermore, we know that our program requires three strings, each of which contains one of the coefficients for the quadratic equation. So this method will build the array of values, but delegate the actual input to yet another method.

  1. Declare a String variable and initialize it with the result of calling getCoefficient("a", kbd). What do you thing the "a" is going to be used for?

  2. Declare two more String variables and assign values in the same way.

  3. Now that you have collected the three coefficients, we must package them in an array to return to the caller. Java makes it easy to declare and initialize an array in a single step. Use the following pattern to create a String[] array.

  4. Return this variable by modifying the return statement.

  type[] variable-name = { list-of-values-separated-by-commas };

Implementing getCoefficient()

Notice that when Eclipse created the empty implementation of getCoefficient() that it named the first parameter string (note lower case). Since your call passed a literal (e.g. "a"), instead of a variable, Eclipse chose a simple name that really doesn’t convey what the parameter is used for. The parameter will be used to prompt the user which coefficient to enter. To make this clearer, change the name of the first parameter to prompt.

A prompt like "a" is not, by itself, very useful. Wouldn’t it be better to give a more complete prompt to the user? There are two ways you might do this:

  1. Pass a longer prompt in the call.

  2. Compose a longer prompt incorporating the parameter.

You will do the latter to print a prompt for the user like the following. Note that the prompt should be on the same line as what the user types.

  Enter coefficient X: <user types value here>

To complete the routine, you will use the kbd.nextLine() method to take a value from the keyboard and assign it to a String variable. You will then modify the return statement to actually return the variable.

Part C - Choices and Error Handling

In Part B, you replaced using command line parameters to main() by prompting the user for input. In this version, you will give the user the ability to use either method. You will also add some error checking.

The call to main() will always get a non-null value for args. But how can you tell if the user actually passed any command line parameters?

  1. Write some simple debugging code as the first line of main() to examine args under different conditions. What is the answer?

  2. Using your new found knowledge, add an if statement encapsulating the prompt code to only execute under appropriate conditions.

  3. Add some more code to determine if the correct number of parameters was passed to main(). If not, call them method usage(). This is a unix style of telling the user how to run the program if they ran it incorrectly. Again, defer the implementation of usage().

  4. Add code to test the value of the a coefficient to verify it is not zero. If it is, call usage().

Implementing usage()

The idea of a usage statement is to give the user a brief description of how to run the program. It is not a replacement for a manual, but serves to remind the user how to use the program. After the usage is given, the program normally terminates itself. To implement the usage() method:

  1. Add output that describes how the user may run the program. All output goes to System.err instead of the System.out. All the methods you have used with System.out are also available to System.err.

  2. Describe the two variations of how to run the program

  3. Define any constraints on the values.

  4. Terminate the program using System.exit(1);. A non-zero exit is the unix convention of signaling an error to the outside world. Zero means successful completion; non-zero signals error (potentially defining the type of error by the number).

Handling non-numeric inputs

Run your program, but use a non-numeric value for a coefficient. What did you observe and where did the error occur? A crashing program is not very friendly to the user. That is why the check for a non-zero value of the coefficient a is important.

To handle this type of error, you must do some checking before invoking the code to convert the String to a double. This most easily done by adding a level of indirection in the code. Instead of calling Double.parseDouble() directly, replace it by a call to a new method you write. Name this method getDouble() and create an empty implementation. Note that in doing this, the basic structure of main() has not changed at all. Only the detail of numeric conversion has changed.

Implementing getDouble()

Like the method Double.parseDouble() your method getDouble() takes a String as a parameter and returns a double. As part of its implementation it will actually use Double.parseDouble() to do the work. But, before that it needs to validate that the input parameter (a String) really does represent a double.

The Jave Scanner class that you used earlier provides this capability. The general flow of the function is:

  1. Define a new Scanner variable initialized using the parameter of the method.

  2. Use one of the Scanner methods to determine if there is a double in the string. Hint: look for method that begins with the prefix hasNext.

  3. If there is a double, extract if from the Scanner variable.

  4. If there is not, print out a message to System.err indicating the given string is not a number and exit using usage().

  5. Now verify if another token was entered by using the Scanner method hasNext(). If it does, print out a message indicating the problem and exit using usage().

  6. Lastly, if all error checks pass, return the double value to the caller.

Error handling during keyboard input

Your program terminates whenever an error condition is encountered. In the case of interactive use, it would be useful to allow the user to correct the mistake, rather that having to start over. Recall that interactive input uses the method getCoefficient().

Add some code to getCoefficient() to verify that what the user entered is valid. Notice that this will be almost identical to what you did in getDouble(), except that you don’t want to terminate the program. Instead, print the error for the user, then print a new prompt and get a revised value.

Perhaps you can take most of the code out of getDouble() and put it in a boolean method the determines if the String is really a double and return a true/false result. Boolean methods often begin with the prefix is so that an if statement invoking it looks like if (isXXX()). You may invoke your new method from both getDouble() and getCoefficient(). Each of these method will ultimately do something different depending on the return value, but use a common method to determine if there are any errors.

Part D - Imaginary Solutions

Not all value for the coefficients of a quadratic equation result in real values for solutions. Under some conditions, the roots will be imaginary. In this case the roots are represented as complex numbers. See this page for how complex roots are written.

Your task is to modify your program to handle such situations without error. It will likely involve modifying printRoot() to handle both real and imaginary roots, or creating a second method to handle imaginary roots. The choice is yours. What else do you need to change?