J David Eisenberg
Posted on February 27, 2022
I teach a beginning Computer Science course at a local community college, and we had just written a Java program that asked the user for the coefficients a
, b
, and c
of a quadratic equation and then used the quadratic formula
to solve the equation.
At that point, I said, “It would be really nice if we printed out the equation being solved,” and started writing the code. Here’s what happened in the development process.
The First Attempt
I started by adding this code directly in main()
:
System.out.printf("The equation is %.3fx^2 + %.3fx + %.3f = 0\n",
a, b, c);
When a
is 7.5, b
is -3, and c
is 2.4, that produced
7.500x^2 + -3.00x + 2.400
The First Improvement
That worked, but having the + and - signs next to each other was a little bit ugly. I wanted to do better, so I split printing the equation into a separate method. I thought, “Oh, this method won’t be too difficult to write.” It turns out that doing an OK job of it is fairly easy. Doing it really well is quite a bit more complicated.
We did some quick planning out loud in class, and came up with the first version of the printFormula()
method. Instead of always printing out a plus sign to separate the b
and c
coefficients, it prints the sign explicitly and then the number without a sign by making sure the value is always positive:
public static void printFormula(double a, double b, double c) {
System.out.printf("%.3fx^2", a);
/* Check b to see if it's + or - */
if (b < 0) {
System.out.printf(" - %.3fx", -b); // -b to avoid two minus signs in a row
} else {
System.out.printf(" + %.3fx", b);
}
if (c < 0) {
System.out.printf(" - %.3f", -c);
} else {
System.out.printf(" + %.3f", c);
}
}
Here are some results for various values of a, b, and c:
a | b | c | prints |
---|---|---|---|
7.5 | -3 | 2.4 | 7.500x^2 - 3.000x + 2.400 |
-4.2 | 5 | 3 | -4.200x^2 + 5.000x - 3.000 |
1 | 0 | 1 | 1.000x^2 + 0.000x – 1.000 |
-1 | 1 | 0 | -1.000x^2 + 1.000x + 0.000 |
The first two equations look decent, but the last two aren’t handling coefficients of -1, 1, and 0 very well. That’s to be expected, as the code was the result of a very short amount of planning near the end of the class session.
I wrote the rest of the code you’ll see in this article at home, as I had promised the class I would work on it over the weekend.
Thinking Things Through Thoroughly
It would be better if the program printed the equations without all those unnecessary decimal places:
7.5x^2 – 3x + 2.4
-4.2x^2 + 5x – 3
x^2 – 1
-x^2 + x
To make this happen, we need to do a very careful analysis of what to print for each of the coefficients a
, b
, and c
when the coefficient is -1, 0, 1, other positive numbers, and other negative numbers. The case of zero is the same for all the coefficients—don’t print anything at all. Here's the rest of the analysis, with commas separating the items we have to print:
Coefficient Value | a | b | c |
---|---|---|---|
1 | "x^2" | " + x" | " + 1" |
-1 | "-x^2" | " - x" | " - 1" |
other positive | a, "x^2" | " + ", b, "x" | " + ", c |
other negative | "-", (-a), "x^2" | " - ", (-b), "x" | " - ", (-c) |
We can handle the problem of “too many digits after the decimal point” by using System.out.print()
instead of System.out.printf()
because System.out.print()
uses only as many decimal places as needed.
Here’s the code that results from using this new plan. Also, we’re printing the equation, not the formula, so let’s change the method name to be accurate:
public static void printEquation(double a, double b, double c) {
/* Handle coefficient for a */
if (a == -1) {
System.out.print("-x^2");
} else if (a == 1) {
System.out.print("x^2");
} else if (a < 0) {
System.out.print("-" + (-a) + "x^2");
} else if (a > 0) {
System.out.print(a + "x^2");
}
/* Now output coefficient b */
if (b == -1) {
System.out.print(" - x");
} else if (b == 1) {
System.out.print(" + x");
} else if (b < 0) {
System.out.print(" - " + (-b) + "x");
} else if (b > 0) {
System.out.print(" + " + b + "x");
}
/* And coefficient c */
if (c == -1) {
System.out.print(" - 1");
} else if (c == 1) {
System.out.print(" + 1");
} else if (c < 0) {
System.out.print(" - " + (-c));
} else if (c > 0) {
System.out.print(" + " + c );
}
}
Now the output looks much better for our examples:
a | b | c | prints |
---|---|---|---|
7.5 | -3 | 2.4 | 7.5x^2 - 3.0x + 2.4 |
-4.2 | 5 | 3 | 4.2x^2 + 5.0x - 3.0 |
1 | 0 | 1 | x^2 – 1 |
-1 | 1 | 0 | -x^2 + x |
Do you see that we really had to plan this out in detail? Can you imagine how long it would have taken to come up with this code if we hadn’t laid out all the details first, but instead sat down at the keyboard and tried things out until we got what we wanted? Yes, you might be able to figure this out in your head and keep it all organized, but making the detailed table helped me a lot.
Eliminating Repetition
The code works, but it looks very repetitive. Let’s look for some common factors that we can combine:
- There’s not much the cases for
a
have in common. - For
b
andc
, the minus or plus sign will have a space before and after it no matter what. We could handle the sign as a separateif
/else
. - If we take the absolute value of the coefficient, then we don’t have to worry about
-b
vs.b
. This is something I would not expect students to recognize—the only reason I recognize it is because I have a lot of experience programming and have seen that theMath.abs()
absolute value method often solves this sort of problem. - We could rewrite the first two rows of column c in our analysis table as
" - ", 1
and" + ", 1
; then they are no longer special cases. Again, this is something students might not see right away; it’s something that comes with experience.
I’m also going to change "x^2"
to "x²"
using the Unicode character \u00b2
so it looks better. If you are on Windows, this may not show up as a superscript 2 if you don’t have your terminal set to use the Unicode character set. I’ve also added an " = 0"
at the end of the equation. Now the code looks like this:
public static void printEquation(double a, double b, double c) {
if (a == -1) {
System.out.print("-x\u00b2");
} else if (a == 1) {
System.out.print("x\u00b2");
} else if (a < 0) {
System.out.print("-" + (-a) + "x\u00b2");
} else if (a > 0) {
System.out.print(a + "x\u00b2");
}
/* Handle sign of b */
if (b < 0) {
System.out.print(" - ");
} else if (b > 0) {
System.out.print(" + ");
}
/* and then its value */
b = Math.abs(b); // force it to be positive
if (b == 1) {
System.out.print("x");
} else if (b != 0) {
System.out.print(b + "x");
}
if (c < 0) {
System.out.print(" - " + (-c));
} else if (c > 0) {
System.out.print(" + " + c);
}
System.out.print(" = 0");
}
Here’s the output from the examples now:
a | b | c | prints |
---|---|---|---|
7.5 | -3 | 2.4 | 7.5x² - 3.0x + 2.4 = 0 |
-4.2 | 5 | 3 | 4.2x² + 5.0x – 3.0 = 0 |
1 | 0 | 1 | x² – 1.0 = 0 |
-1 | 1 | 0 | -x² + x = 0 |
Finishing Touches
Let’s evaluate the output of this latest version. The next to last example has 1.0 instead of 1; this happened when we combined the cases for coefficient c
. We should fix that. In fact, we should get rid of the “.0” for integral coefficients everywhere.
Unlike the revised code for coefficient c
, which is significantly shorter, the code for coefficient b
is not only three lines longer than the code it replaced, it’s inconsistent with the code for the other two coefficients, and it’s harder to read.
We didn’t do anything wrong by looking for commonalities and trying to make the code more readable; it just didn’t turn out to be the better thing to do in this case. Oh well, live and learn.
Back to the problem at hand: getting rid of the “.0” for integral coefficients. We can test to see if a double
coefficient has an integer value by asking if the number equals the integer part of the number. If so, the fractional part must be zero, and we print the integer part. That won’t have a decimal point in it. If there is a fractional part, we print the number, unchanged.
We need to do this every time we print a coefficient. Here is a place where it is definitely worth our time to write a new method that does this operation, using a cast to convert the double
to int
if necessary:
public static void printNumber(double number) {
if (number == (int) number) {
System.out.print((int) number);
} else {
System.out.print(number);
}
}
We then change the code to use this new method. We also use the shorter, more consistent code we wrote to handle coefficient b
. Here’s the method with all those changes:
public static void printEquation(double a, double b, double c) {
if (a == -1) {
System.out.print("-x\u00b2");
} else if (a == 1) {
System.out.print("x\u00b2");
} else if (a < 0) {
System.out.print("-");
printNumber(-a);
System.out.print("x\u00b2");
} else if (a > 0) {
printNumber(a);
System.out.print("x\u00b2");
}
if (b == -1) {
System.out.print(" - x");
} else if (b == 1) {
System.out.print(" + x");
} else if (b < 0) {
System.out.print(" - ");
printNumber(-b);
System.out.print("x");
} else if (b > 0) {
System.out.print(" + ");
printNumber(b);
System.out.print("x");
}
if (c < 0) {
System.out.print(" - ");
printNumber(-c);
} else if (c > 0) {
System.out.print(" + ");
printNumber(c);
}
System.out.print(" = 0");
}
Here are our four example equations:
a | b | c | prints |
---|---|---|---|
7.5 | -3 | 2.4 | 7.5x² - 3x + 2.4 = 0 |
-4.2 | 5 | 3 | -4.2x² + 5x - 3 = 0 |
1 | 0 | 1 | x² - 1 = 0 |
-1 | 1 | 0 | -x² + x = 0 |
Summary
Here are some of the main takeaways from this code:
- Start with a first attempt at a solution; the idea is to have something—anything—that works.
- You can do a quick analysis of the problem and come up with a better, but still somewhat unsatisfactory, result.
- Instead of doing a quick analysis, do a more careful analysis of the problem to produce a much better solution.
- Look for ways to eliminate duplicate code. Sometimes, as in the case of coefficient
c
, what you get is an improvement. Sometimes, as in the case of coefficientb
, it isn’t. - Create new methods when you have to do an operation many times. We did this when we wanted to avoid printing decimal places for integer coefficients.
- “Wow! For a ‘simple’ problem, that’s a lot of work!” Yes, it is. Getting a program to work well requires a lot of analysis. Then it’s a process of writing code, testing it, seeing that it works, and making improvements—lather, rinse, repeat.
Posted on February 27, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 29, 2024