Learn Programming in Java

<<Previous | ToC | Next >>

Simple Calculator

I told you "no math" up front, what gives with the calculator? Not to worry, the computer does the math. But instead you get to worry about reading keyboard input in the native Java manner (and coping nicely with typing errors) because my Zystem input methods don't read numbers with decimal points. It's good for you, you can't lean on my Zystem crutch forever. Well, you can, but other programmers will wonder about you. Anyway, (later) today you get to program exceptions properly.

For a simple 4-function calculator we can do it as a simple loop:

while (true) {
  /// a. display the current result (initially =0)
  /// b. accept a command letter:
  /// c. if 'q' quit
  /// d. if 'c' clear
  /// e. accept a value
  /// f. if command is '+' add
  /// g. else if '-' subtract
  /// h. else if '*' multiply
  /// i. else if '/' divide
  /// j. otherwise print error message
  } // end of main loop
You should be able to write this whole program without any help, if you use the Zystem input methods you already know about. Try it now, before reading any farther.

What data type did you choose for your display value? If you chose "int" what happens when you input or calculate fractional numbers? Java has two data types that work with non-integer numbers, float and double, but nobody uses float any more, it was initially designed for computer hardware with limited memory and/or that ran double width numbers slower than single. The computer in your pocket (it's called a "smart phone") has a thousand times more computer memory than all the first-generation (model 704) super-computers IBM ever made put together, so that's no longer a problem. Most of the math hardware is "pipelined" so the extra time it takes to calculate the wider result is overlapped by other calculations and does not slow anything down most of the time. Because float and double are different internal formats, the programmers got lazy and programmed everything in double, so the hardware designers no longer do the arithmetic in the shorter format, they just convert everything to double, do the math, then convert the result back to float (if that's what you want).

Bottom line: you probably want to use double as the data type whenever your data could extend past a few million (seven decimal digits, or nine if integer) or involve fractions, like for your result and input values in this calculator. That almost never happens in the programs I write, so I mostly use integers for everything. One consequence is that I have no "safe" input routines ready for you to use, you must write your own. Like taking your vitamins, it builds strong healthy bones (in your code ;-)

You could build your calculator in the same Hello class we have been using all along, but I think you are ready to start making your own Java classes. This first one is easy: inside the BlueJ dashboard (the "StartHere" window with the yellow rectangles) right-click the blank space behind those rectangles, or else pull down the Edit menu, and choose "New Class". In the dialog that pops up, give it your class name, like "Calculator" (it must start with a capital letter), verify that it will be making a Java class (later versions of BlueJ have several irrelevant options) and hit OK. Then double-click the new yellow rectangle with your class name.

What BlueJ gives you is a green box outlining the class, with two yellow boxes for methods. I deleted both yellow boxes and in their place (in the white space inside the green box) made a single

public static void main() {

} // end of main

Just like in Hello. Notice that it made a new yellow box for you. This is where your calculator code will go, in the white space inside that yellow box. You will declare your variables on the first few lines there, then put in our -- I mean your -- stub while loop and save it and make sure it compiles. You need to declare a char variable -- I called mine "optor" (short for "operator") but you can call yours anything you like, just when I talk about optor, you substitute your variable name.

OK, let's work on line (b), getting the operator input, for this first cut using Zystem.ReadLetter():

/// b. accept a command letter:
optor = Zystem.ReadLetter();
/// c. if 'q' quit
if (optor == 'q') return;
/// d. if 'c' clear
if (optor == 'c') {
  /// clear your result variable
  continue;} // back to the top of the loop

The user could type pretty much anything, and you (smart programmer that you are becoming) need to cope with it and do something reasonable and not too rude. Let's validate it:

if (optor != '+') if (optor != '-')
    if (optor != '*') if (optor != '/') {
  if (optor > ' ') // just ignore white space
    System.out.println("Please type an operator: + - * / c q");

As I said above, we will start by using integer input, my Zystem.ReadInt(), reading it into either an int variable or a double. Let's start with int (that's the type of the variable on its declaration line). I called mine inval, but this is your program, you decide.

Now to read our input value:

inval = Zystem.ReadInt();

Do you think you can do the rest? Try it. If you have trouble making the calculation work, but it compiles, it will probably run, and you can use the debugger to see what's going wrong. If it won't compile, you will have those red bars in the margin and you can click the error count in the corner for a message. Often only the first error message makes any sense.

After you have it working, you can test whether it's working correctly. Notice that if you give it a fractional value like 12.34, it only sees what's before the decimal point. That's because Zystem.ReadInt() stops reading at the decimal point. We'll fix that shortly. However, if you give it +1234, and then /100, the calculation is in double (you did declare the result that way, right?) so that gets you a fractional result.

What happens if you change the input variable (inval) to be double also?. Do you see any difference? Java automatically promotes integers to double when used in mixed-mode (both integer and double in the same) calculations, so when you read Zystem.ReadInt() into a double variable, it also gets promoted. Otherwise, if you are using a double for your result value, the (still integer) input value gets promoted when you do the arithmetic operation. You need to understand this, because the data types of the values you are using sometimes makes a difference, and sometimes promotion fixes it for you (like today). You need to understand why. If it's still foggy, ask the instructor, that's what they are here for.

When I ran my version and gave it goofy inputs, it did unexpected things, probably because BlueJ is filtering the keyboard input. But it worked OK when I gave it an operator and a number (with or without a space between them), followed by return ("Enter" on most keyboards). In the next lesson, when you replace my Zystem class with Scanner, you will need to type a space between the operator and its number.

By the way, did you try giving it "/ 0" (divide by zero)?

Next: Exceptions

<<Previous | ToC | Next >>

Revised: 2020 October 21