Java - Simple arithmetic Java - Simple arithmetic [ Sum of two numbers | Primitive datatypes | Operators| Formatted output | Using a standard class | Arithmetic errors ] Sum of Two Numbers This program simply adds two integers and displays the sum. public class Sum1 { static public void main(String args[]) { int x = 3; int y = 4; System.out.println("Sum of " + x + " and " + y + " is " + (x+y)); } } The program creates two variables, initialises them and prints the sum. The output is: Sum of 3 and 4 is 7 Writing such an apparently simple program requires mastery of several important aspects of programming. These include: Declaration and initialisation of variables Writing to output Converting numbers from internal to external form The int primitive data type The first significant point is the declaration of the variables x and y. int is the variable type. It means, of course integer. int is one of Java's primitive data types. A primitive data type is a data type that, more or less, corresponds to a data type handled by the underlying hardware. More importantly primitive data types are not objects and there are no methods associated with them. If you are familiar with other programming languages you may be wondering what the size range of the integers is. If you are very familiar with programming you might assume that the size of an integer is determined by the size on the underlying hardware. This is incorrect. Java programs are interpreted by the Java Virtual Machine and this imposes a size of 32 bits on int irrespective of what the hardware does. Java also supports 64 and 8 bit integers via primitive data types called long and byte. All integer data types are signed. There are no unsigned data types like those available to a C programmer. For systems work, this is a serious nuisance. Syntax of declaration The names of variables conform to the same conventions as the rules for naming classes that have already been described. The syntax of declarations is, essentially, identical to that used by the C programming language. Note that initialisation is part of the declaration. As well as the simple form shown above, the declarations could have been written all on one line as: int x = 3, y = 4; If you fail to initialise a variable, the consequences depend on the context. The compiler will check that simple (or local) variables such as x and y used in this example have been initialised before they are used. Failure to do so results in a compiler error message. Later we will meet variables that are, by default, initialised to zero (or NULL). Integer constants (literals) Java constants are written using the same syntax as used by the C programming language. Octal and hexadecimal constants are expressed using an initial '0' and an initial '0x' or '0X' respectively. Long constants can be written with an 'L' immediately after the constant. Do not write constants with initial zeroes unless you mean them to be interpreted as octal constants. int september = 09; This results in an error message. (The range of octal digits is 0 to 7). Java programmers usually refer to literals rather than constants. Output of values You should be familiar with the basic output function System.out.println() and may have come to the conclusion that it requires a "string" parameter. This is actually not true as the following version of the program code demonstrates. (print() is an output method similar to println() except that it doesn't generate a new-line character at the end of the line.) public class Sum1 { static public void main(String args[]) { int x = 3, y = 4; System.out.print("Sum of "); System.out.print(x); System.out.print(" and "); System.out.print(y); System.out.print(" is "); System.out.println(x+y); } } Rather than print() being called with parameters of various different types, what is actually happening is that the class System.out includes several print() functions (or methods), one for each type of parameter. The compiler inspects the parameter type and determines which function to call. In this particular case the version of print() that takes an int parameter is used. This is called method overloading. In the first version of this program the output was generated using a single call of System.out.println() thus: System.out.println("Sum of " + x + " and " + y + " is " + (x+y)); This demonstrates the overloading of the + operator to mean string concatenation and the implicit casting of the integer variables x and y to equivalent decimal strings. It also demonstrates some aspects of operator precedence. The parameter of the println() function call is an expression which evaluates to a string. The expression consists of six values, one of which is a subexpression. The values are: Name/value How value is determined "Sum of" String constant x Integer variable preceded by string constant and string concatenation operator, so must be converted (or cast) to a string value. " and " String constant y Integer variable preceded by string concatenation operator, cast implied. " is " String constant (x+y) Integer expression There are two other important points to note here. The expression that is a parameter of the function System.out.println() is evaluated left to right. The sub-expression (x+y) is evaluated before the main expression. This higher precedence is implied by the enclosing parentheses. Since the subexpression consists of two integer variables, the + symbol, in this case, represents the integer addition operator. It is instructive to modify the program by omitting the parentheses enclosing the sub-expression (x+y) giving the code. public class Sum1 { static public void main(String args[]) { int x = 3, y = 4; System.out.println("Sum of " + x + " and " + y + " is " + x+y); } } In this case the output is: Sum of 3 and 4 is 34 What has happened here is that the evaluation of the System.out.println() parameter expression has simply preceded from left to right so after processing " is " the next operator (+) is seen as a string concatenation operator so x is cast to a string and concatenated followed immediately by the casting and concatenation of the value of y. Primitive datatypes Java supports a number of primitive data types other than the int type discussed above. The full list is: Name Description Range Literal (constant) syntax boolean Holds the value true or false. Occupies 1 bit. true, false true, false char Holds a single unicode character. Occupies 16 bits. '\u0000' to '\uffff' Always enclosed by single quotes (e.g. 'a'). Uses escaping conventions such as '\n' for a newline, '\ooo' where o is an octal digit or '\uxxxx' where x is a hexadecimal digit. byte Holds a signed integer. Occupies 8 bits of memory. -128 to 127 A decimal, octal or hexadecimal constant string preceded by a sign, if required. short Holds a signed integer. Occupies 16 bits. -32768 to 32767 int Holds a signed integer. Occupies 32 bits of memory. -2147483648 to 2147483647 (approx 2×109) long Holds a signed integer. Occupies 64 bits. -9223372036854775808 to 9223372036854775807 (approx 9×1019) float Floating point number in accordance with IEEE 754-1985 occupies 32/64 bits. ±1.4E-45 to ±3.4028235E+38 Normal decimal notation or 'E' notation double ±4.9E-324 to ±1.7976931348623157E+308 There are implicit casts between the various numeric primitive types dependant on the context. This means that expressions such as 1 + 2.5 can be written and evaluated. boolean types cannot be converted to/from any other type. It is possible to convert integer types to char but anything other than the least significant 16 bits will be lost; this is a narrowing conversion. If the primitive types give insufficient precision then the package java.math includes the class BigDecimal which provides arbitrary precision arithmetic, at a price in performance. Operators The full set of Java operators are given below. Precedence Associativity Symbol Operand types Description 15 Left to Right . object,member object member access [ ] array,int array element access (parameters) method,parameter list function/method invocation ++ -- numeric variable post increment, post decrement 14 Right to Left ++ -- numeric variable pre increment, pre decrement + - numeric value unary plus, unary minus ~ integer bitwise complement ! boolean not 13 Right to Left new class, parameter list object creation (type) any cast (explicit type conversion) 12 Left to Right * / % numeric values multiplication, division, remaindering 11 Left to Right + - numeric values addition, subtraction + string, any string concatenation 10 Left to Right << integer, integer bit shift left >> integer, integer bit shift right with sign extension >>> integer, integer bit shift right with zero extension 9 Left to Right < <= numeric values less than, less than or equals > >= numeric values greater than, greater than or equals instanceof reference, type type comparison 8 Left to Right == primitive data types is equal to != primitive data types is not equal to == references refer to same object != references do not refer to same object 7 Left to Right & integers bitwise AND booleans boolean AND 6 Left to Right ^ integers bitwise exclusive OR (XOR) booleans boolean exclusive OR (XOR) 5 Left to Right | integers bitwise OR booleans boolean OR 4 Left to Right && booleans conditional AND 3 Left to Right || booleans conditional OR 2 Right to Left ?: boolean,any,any trinary operators, dependant on boolean value yields second or third operand as value. 1 Right to Left = variable, any assignment *= /= %= += -= <<= >>= >>>= &= ^= |= variable, any assignment with operation More Arithmetic Examples Here's a simple example that demonstrates mixed integer and floating point arithmetic. public class Arithex1 { static public void main(String args[]) { double x = 23.4, y = -41.7; int i = 4, j = 7; System.out.println("x + y = " + (x+y)); System.out.println("x * i = " + (x*i)); System.out.println("j / i = " + (j/i)); System.out.println("x % i = " + (x%i)); } } And here's the output: x + y = -18.300000000000004 x * i = 93.6 j / i = 1 x % i = 3.3999999999999986 Note that the division of j by i has yielded the value 1 rather than the 1.75 that you might have expected. The reason for this is that both the operands of the division operator are int so the operation performed is integer division which yields an integer result. Note also that the number of decimal places given for floating point numbers seems to vary widely. This is far from satisfactory except for simple proof of concept programs. Simple output format controls Output layout control in Java is much less straightforward then the simple "picture" or "field definition" driven formatting available to programmers using languages such as C, Cobol or Fortran. The theory is, perhaps, that formatting is so complex, especially using proportionally spaced fonts, that it sould be left to separate utilities such as WWW browsers. Java programs should just generate suitably marked up strings and leave screen painting programs to position everything exactly where the user wants it in accordance with local preferences and display capabilities. This theory is somewhat faulty as a quick examination of the above output will show. Clearly the user would reasonably expect something like -18.3 or possibly -18.30 rather than the -18.300000000000004 he actually got. This requires significant sophistication on the part of the output processing program to trim the string down to the correct size. It makes much more sense for the Java program to generate strings with the correct number of decimal places in the first place. Unfortunately this is not particularly straightforward, but here's the code. import java.text.*; public class Arithex2 { static public void main(String args[]) { NumberFormat layout = NumberFormat.getNumberInstance(); layout.setMaximumFractionDigits(4); double x = 23.4, y = 41.7; System.out.println("x/y = " + (x/y)); String outstr = layout.format(x/y); System.out.println("x/y = " + outstr); } } And here's the output: x/y = 0.5611510791366906 x/y = 0.5612 The first line shows the output as generated directly by System.out.println() and the second shows it with the output rounded to four decimal places. Achieving the rounding to four decimal places requires the use of the Java class NumberFormat and some of its associated methods. Since this is the first example that has used one of Java's extensive set of standard classes it will repay further examination. Using a Standard Class When using a standard class it is, more or less, essential to consult the documentation. A full set of documentation is available locally at http://www.scit.wlv.ac.uk/appdocs/java/. However there is a lot of this documentation and finding your way round it may be a bit of a challenge. The number formatting task is clearly text related and this suggests that the java.text package (group of classes) will be a suitable place to look. A summary of the package contents is available at http://www.scit.wlv.ac.uk/appdocs/java/api/java/text/package-summary.html and this reveals the existence of a class called NumberFormat which is described by the phrase "NumberFormat is the abstract base class for all number formats". The first thing that the programmer needs to do is import the java.text package into this program. This is done using the code: import java.text.*; The asterisk means import all the classes in the java.text package. The programmer could have written import java.text.NumberFormat; but most programmers choose to import the whole package. A quick skim of the documentation suggests that the program needs an object of type NumberFormat. However this is not quite right. NumberFormat is an abstract class, which means that you can't simply create 'instances'. The reason, in this case, is that the class includes support for formatting a variety of different data types. Particular formatting support objects are created by class methods such as getCurrencyInstance(), getPercentInstance() and getIntegerInstance(). In this case we want getNumberInstance. The code NumberFormat layout = NumberFormat.getNumberInstance(); creates an object of type NumberFormat called layout. An object is really nothing more than a composite (non-primitive) data type. Methods are functions that can operate on the object's internal structure; the method definitions are 'hidden' in the class definition (as is the object's internal structure). Further reference to the documentation reveals that the number of decimal places can be set using the setMaximumFractionDigits() method of the NumberFormat class which takes an int parameter. It is used here via the code layout.setMaximumFractionDigits(4); Note. For a really professional piece of work, the setMinimumFractionDigits(), setMaximumIntegerDigits() and setMinimumIntegerDigits() methods should also be used. The final step is to do the actual formatting. The method format() that generates the output string uses the code: String outstr = layout.format(x/y); Arithmetic Errors It is often overlooked, especially by non-technical people, that computers do not always get their sums right. This can happen for a variety of reasons such as the result being outside the range of the data type, input data being faulty or the operation being impossible (such as divide by zero). A professional programmer will be aware of these possibilities and will code so that errors are either avoided or detected and reported. You may have noticed in the earlier example on this page that the answer -18.3000000000000004 as well as being inconveniently long was also wrong, the correct answer being -18.3. The problem here is that it is not possible, using the binary based IEEE 754 representation to store the decimal number -18.30 exactly. Here's a simple Java example designed to explore what happens if you try to divide by zero. public class Divzer { static public void main(String args[]) { double x,y,quotient; x = 1; y = 0; quotient = x/y; System.out.println("Quotient = " + quotient); } } The output is rather surprising: Quotient = Infinity This is actually correct, if not particularly helpful. The IEEE floating point data representation mandated by the Java specification specifies a distinct representation for ∞. The specification even includes details as to the results various arithmetic operations yield when one of the operators is ∞. Here's the same thing with integer variables rather than floating point variables. public class Divzer { static public void main(String args[]) { int x,y,quotient; x = 1; y = 0; quotient = x/y; System.out.println("Quotient = " + quotient); } } The output, however, is quite different. Exception in thread "main" java.lang.ArithmeticException: / by zero at Divzer.main(Divzer.java:9) This is a typical Java error message. In the first case the error could be caught or trapped by a simple arithmetic test as shown below. (Java's if statement will be discussed in more detail later). public class Divzer { static public void main(String args[]) { double x, y, quotient; x = 1; y = 0; quotient = x/y; if(quotient == Double.POSITIVE_INFINITY) System.out.println("Error"); else System.out.println("Quotient = " + quotient); } } Notice the constant Double.POSITIVE_INFINITY, this is taken from the Double wrapper class. You can't just write ∞ in your source code. As well as ∞ the IEEE floating point format can also take values that are printed as "NaN", which literally means "Not a number". It may arise if an arithmetic operation with an undefined value is attempted. It can also appear as a consequence of non-numeric data somehow finding its way into a memory location supposedly holding numeric data. This is most unlikely to happen when using a programming language such as Java. A wrapper class is a class whose methods are designed to make objects of one type look like and behave like objects of another type. There are also wrapper classes associated with the primitive data types so that primitive data types can be used in contexts where objects are required. Some modern object-oriented programming languages such as Microsoft's C# avoid this complexity by abolishing primitive data types and treating everything as an object. The presence of primitive data types means that Java is not a 'pure' object-oriented programming language. The integer case is handled rather differently. Java errors can be caught by a standard mechanism called try/catch. A try clause encloses the code which may cause an error. Java programmers refer to code throwing an exception. catch clauses are used to catch particular types of exception. Here's the code: public class Divzer { static public void main(String args[]) { int x, y, quotient; x = 9; y = 0; try { quotient = x/y; } catch (ArithmeticException e) { System.out.println("Problem"); System.exit(1); } System.out.println("Quotient = " + quotient); } } In this case the catch clause is executed when an ArithmeticException exception is thrown in the immediately preceding try clause. In this simple example the catch just prints Error and exits via the return statement. Note that the catch clause is associated with an object that has to be named (ae). This looks pointless here but for many sorts of exception it is rather useful as further information about the error is available via the object properties. Unfortunately this mechanism won't work for floating point arithmetic since division by zero doesn't generate an exception; it yields ∞. In fact, the mechanism shown above doesn't even catch all integer arithmetic errors as the following example shows. public class Intof { static public void main(String args[]) { int x, y, product; x = 1000000; y = 2000000; try { product = x*y; } catch (ArithmeticException e) { System.out.println("Problem"); System.exit(1); } System.out.println("Product = " + product); } } which produced the output: Product = -1454759936 This is just simply wrong. An elementary acquaintance with computer architecture reveals that the actual product (2×1012) is simply too big to fit in the int data type (which has a maximum storage capacity of ~2×109) and the result has been truncated to the 32 least significant bits. This has, as you will have noticed, not thrown an arithmetic exception. The Java definition makes it quite clear that such integer arithmetic errors will not be detected or cause exceptions. The only way to avoid errors of this sort is by careful, very careful, program design. The failure of the Java Virtual Machine specification to include the detection of such errors may be a consequence of some hardware platforms not being capable of detecting such errors. On most platforms the processing core of the computer detects such errors and raises a hardware error condition which can be detected/caught by suitably written operating systems and application software. The basic hardware mechanism is often called a trap and is usually handled in the same way as the handling of unexpected external events (such as I/O traffic) via the interrupt mechanism which is well described in most hardware texts. This page is part of a set of notes about the Java programming language prepared by Peter Burden for the module CP4044. A disclaimer applies to this page.