We will now write a simple calculator program that can perform the four basic operations: add, subtract, multiply, and divide. The program will prompt user to input a simple expression such as 3 + 7 and the program will output the result. Here is the first version of the program that is capable of adding numbers together.
import java.util.Scanner; public class Calculator { public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.println("Simple Calculator - Enter a simple expression consisting of\ntwo numbers and one operator, separated by spaces (e.g. 7 + 2)."); System.out.print("Expression: "); double operand1 = sc.nextDouble(); String operator = sc.next(); double operand2 = sc.nextDouble(); if (operator.equals("+")) { System.out.println("Result: " + (operand1 + operand2)); } } }
[Run]
Simple Calculator - Enter a simple expression consisting of
two numbers and one operator, separated by spaces (e.g. 7 + 2).
Expression: 18 + 35
Result: 53.0
These are the steps the program takes:
- Line 5 sets up a Scanner.
- Line 7 prints an instructional message. The string contains a
\n
which is why the message is split into two lines in the sample run. - Line 8 prompts the user to enter an expression.
- Line 9 blocks for input. In the console, the expression 18 + 35 is entered. From the
Scanner
’s point of view, we have just entered three tokens (because theScanner
likes to separate things by spaces). The first token is 18, the second is +, and the third is 35. Remember that theScanner
’s variousnextX
methods (exceptnextLine
) only attempt to read one token. Therefore, thenextDouble
on line 9 only reads the first token (18). It is then stored in variableoperand1
. - On line 10, because there’s still two tokens left, the
next
method immediately reads the second token (+), and therefore doesn’t need to block for input. The + is stored in variableoperator
as aString
. - Likewise, line 11 immediately reads the third token (35). This goes in
operand2
. - On line 13 is the start of an if statement. It simply checks if
operator
contains a +.- If true, line 14 simply adds
operand1
andoperand2
together and prints the result (53.00).
- If false, the program ends without doing anything.
- If true, line 14 simply adds
This approach means spaces are necessary otherwise the Scanner
will read too much at once and the program won’t function correctly or, more likely, will crash. For example, inputting 7+ 3 won’t work because 7+ is a single token and cannot be interpreted as a double
on line 9, resulting in an exception (error). Now let’s add the other three operations.
import java.util.Scanner; public class Calculator { public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.println("Simple Calculator - Enter a simple expression consisting of\ntwo numbers and one operator, separated by spaces (e.g. 7 + 2)."); System.out.print("Expression: "); double operand1 = sc.nextDouble(); String operator = sc.next(); double operand2 = sc.nextDouble(); if (operator.equals("+")) { System.out.println("Result: " + (operand1 + operand2)); } else if (operator.equals("-")) { System.out.println("Result: " + (operand1 - operand2)); } else if (operator.equals("*")) { System.out.println("Result: " + (operand1 * operand2)); } else if (operator.equals("/")) { System.out.println("Result: " + (operand1 / operand2)); } else { System.out.println("Invalid operation."); } } }
Lines 16-27 are new. Lines 16-24 consist of three else if
blocks that do the same thing as the first if
block, except they check for a different symbol and perform a different operation. The else
block on lines 25-27 will run if none of the others blocks match and will print “Invalid operation.”
[Run]
Simple Calculator - Enter a simple expression consisting of
two numbers and one operator, separated by spaces (e.g. 7 + 2).
Expression: 6.8 * 7.74
Result: 52.632
There is a small problem. We’re using double
, which is a floating-point type, so this can happen:
[Run]
Simple Calculator - Enter a simple expression consisting of
two numbers and one operator, separated by spaces (e.g. 7 + 2).
Expression: 0.3 – 0.2
Result: 0.09999999999999998
That result is an atom away from 0.1 but it’s not good enough visually. We could use printf
to round the result before printing, and use the format specifier %.7f
so we always get 7 digits after the decimal point. But then the result of 0.3 – 0.2 would be formatted as 0.1000000, which, while technically correct, still looks unprofessional.
DecimalFormat
A better alternative is to use the built-in class DecimalFormat
. This class is used to format decimal numbers. Like the Scanner
, we need to create an object/instance of the DecimalFormat
class and use that. We also supply it a pattern that specifies how it should format decimal numbers. We can then pass any results into the format
method to obtain the formatted equivalent. Here is the updated code:
import java.text.DecimalFormat; import java.util.Scanner; public class Calculator2 { public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.println("Simple Calculator - Enter a simple expression consisting of\ntwo numbers and one operator, separated by spaces (e.g. 7 + 2)."); System.out.print("Expression: "); double operand1 = sc.nextDouble(); String operator = sc.next(); double operand2 = sc.nextDouble(); DecimalFormat df = new DecimalFormat("0.#########"); if (operator.equals("+")) { System.out.println("Result: " + df.format(operand1 + operand2)); } else if (operator.equals("-")) { System.out.println("Result: " + df.format(operand1 - operand2)); } else if (operator.equals("*")) { System.out.println("Result: " + df.format(operand1 * operand2)); } else if (operator.equals("/")) { System.out.println("Result: " + df.format(operand1 / operand2)); } else { System.out.println("Invalid operation."); } } }
Line 1 imports the DecimalFormat
class, which is necessary to use it. Line 14 declares a variable called df
(short for DecimalFormat
). Its type is DecimalFormat
meaning df
can reference (hold) a DecimalFormat
object. The right side is what actually creates a DecimalFormat
object using the new
keyword. Between the parentheses is the string “0.#########”. This string is the pattern that the DecimalFormat
will follow when formatting a decimal number. As we can see, the pattern consists of 9 pound/hash (#) signs to the right of the decimal point (the fractional part) and 1 zero to the left of the decimal point (the integral/integer part). Hashes represent an optional digit, meaning that if we give DecimalFormat
a number, it will give back the number with the fractional part rounded up to 9 digits. A zero represents a required digit. The integer part is always wholly given regardless of the pattern, but the zero ensures at least one digit will appear where DecimalFormat
may omit it otherwise.
Let’s look at line 4 where df
is used. Line 4 still calculates operand1
+ operand2
like normal but it’s placed within the format
method. This means the result of operand1
+ operand2
goes into the method, which, in turn, formats it and then returns the formatted result. Then the print statement simply prints it. Lines 20, 23, and 26 do the exact same thing but for the other three operators (subtraction, multiplication, division). To make sure it works, let’s go through some test runs:
[Run 1]
Simple Calculator - Enter a simple expression consisting of
two numbers and one operator, separated by spaces (e.g. 7 + 2).
Expression: 511.295 + 1374.842
Result: 1886.137
[Run 2]
Simple Calculator - Enter a simple expression consisting of
two numbers and one operator, separated by spaces (e.g. 7 + 2).
Expression: 0.3 – 0.2
Result: 0.1
[Run 3]
Simple Calculator - Enter a simple expression consisting of
two numbers and one operator, separated by spaces (e.g. 7 + 2).
Expression: 100 / 34
Result: 2.941176471
All three runs give correct and properly formatted results. Remember that the hashes in the pattern represent optional digits so a formatted number will have anywhere between 0 and 9 digits (after the decimal point). So, Run 1 gives us 1886.137. Run 2 gives us 0.1 this time due to rounding, instead of 0.09999999999999998. Run 3 results in a recurring number but it is rounded to 9 decimal places as the patterns specifies no more than 9 digits. DecimalFormat
can be used to achieve various formats. Here are some more examples of patterns and their effect on input:
Input | Pattern | Output | Explanation |
---|---|---|---|
0.1283796 | 0.### | 0.128 | Integer: 1 digit minimum. Fraction: 3 digits optional. |
5 | 0.### | 5 | Integer: 1 digit minimum. Fraction: 3 digits optional. |
7.369711 | 0.### | 7.37 | Integer: 1 digit minimum. Fraction: 3 digits optional. |
0.915 | 0.00 | 0.92 | Integer: 1 digit minimum. Fraction: 2 digits exactly. |
3.915 | #.00 | 3.92 | Integer: 1 digit optional. Fraction: 2 digits exactly. |
0.915 | #.00 | .92 | Integer: 1 digit optional (shows nothing if 0). Fraction: 2 digits exactly. |
522.915 | 0.000000## | 522.915000 | Integer: 1 digit minimum. Fraction: 6 digits minimum and 2 optional. |
522.91584176 | 0.0000## | 522.915842 | Integer: 1 digit minimum. Fraction: 4 digits minimum and 2 optional. |
8.915 | 00000.00000 | 00008.91500 | Integer: 5 digits minimum. Fraction: 5 digits exactly. |
0.9756962 | 0.0#% | 97.57% | Integer: 1 digit minimum. Fraction: 1 digit minimum and 1 optional. Input is expected to be normalised (between 0 and 1) because it gets multiplied by 100. |
1000000 | ,##0.00### | 1,000,000.00 | Integer: 1 digit minimum. Add comma every third digit. Fraction: 2 digits minimum and 3 optional. |
612447.817 | €0.00### | €612447.817 | Integer: 1 digit minimum. Add symbol (€) to the front. Fraction: 2 digits minimum and 3 optional. |
58377429.147 | £,##0.00 | £58,377,429.15 | Integer: 1 digit minimum. Add comma every third digit. Add symbol (£) to the front. Fraction: 2 digits exactly. |
0.10 | #.## | 0.1 (not .1) | Works but is somewhat misleading. Typically, a leading or trailing 0 will be omitted where there’s a #. I think Java reinterprets the pattern as 0.## behind the scenes. |