Arithmetic and logical operators

Figure 111. The binary plus operator Slide presentation
The binary plus operator

Figure 112. Binary operator output type Slide presentation
Binary operator output type

exercise No. 34

Calculating a circle's area

Q:

The area a of a given circle having radius r is being obtained by a = π × r 2 . Complete the following code to calculate the result and write it to standard output using System.out.println(...):

public static void main(String[] args) {

   double radius = 2.31;   // A circle having a radius (given e.g. in mm).
   double pi = 3.1415926;  // Constant relating a circle's radius,
                           //perimeter and area.
 
  System.out.println( /* TODO: Write the circle's area in terms of both variables radius and pi */);
}

Tip

You may want to read the overview section on statements in [Kurniawan].

A:

Directly using the area calculating expression
public static void main(String[] args) {

   double radius = 2.31;   // A circle having a radius (given e.g. in mm).
   double pi = 3.1415926;  // Constant relating a circle's radius,
                           //perimeter and area.

   System.out.println(pi * radius * radius);
}
Using an intermediate variable

Instead of directly using an expression we may assign its value to a variable prior to creating the desired output:

public static void main(String[] args) {

   double radius = 2.31;   // A circle having a radius (given e.g. in mm).
   double pi = 3.1415926;  // Constant relating a circle's radius,
                           //perimeter and area.

   double area = pi * radius * radius;
   System.out.println(area);
}

exercise No. 35

Dividing values

Q:

Consider the following statement:

System.out.println(8 / 9);

The output is 0 rather than 0.888888... . Explain this result.

A:

The divide operator acts on two literals 8 an 9 of type int. Thus irrespective of the possibly intended floating point result 8 / 9 expression's type is int as well.

According to https://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.1.3-300-A-2 the floating point value 0.88888... will be rounded toward zero yielding an int value of 0 rather than 1.

exercise No. 36

Strange things with operator ++

Q:

  1. Consider the following code snippet:

    byte a = 127;
    System.out.println("Current value=" + a + ", now adding 1 to it");
    a++;
    System.out.println("New value=" + a);

    This will produce the following output:

    Current value=127, now adding 1 to it
    New value=-128

    Explain this strange behaviour on bit level.

  2. Moreover the seemingly equivalent code yields a compile time error:

    byte a = 127;
    System.out.println("value=" + a);
    a = a + 1; // Error: Type mismatch: cannot convert from int to byte
    System.out.println("New value=" + a);

    What do you learn about the two operators + an a++? Explain this error's cause.

Tip

You may want to read the overview section on statements in [Kurniawan].

A:

  1. A byte variable ranges from -128 to +127. The ++ operator is being defined as the binary addition of 1 on machine level discarding a possible carryover. Thus incrementing 127 by 1 yields -128 due to 2-complement byte representation:

    01111111 127
    +00000001 +1
    =10000000 =-128
     01111111     127
    +00000001    +  1
    ---------    ----
     10000000    -128

    Conclusion once again: Watch out when doing (integer) arithmetic!

  2. The compile time error is due to the definition of the binary + operator: Given two variables a and b of types char, byte, short or int a possibly mixed expression like a + b is always of type int irrespective of it's operands' types. Consider:

    byte a = 120, b = 10;
    System.out.println(a + b);

    This yields the expected output of 130 corresponding to an int value.

    If the expression a + b was of type byte an arithmetic overflow as in the subsequent code example would occur:

    byte a = 120, b = 10;
    
    byte sum = (byte) (a + b);  // yields -126

    The explicit type conversion (a so called type cast or cast for short) forces the 4-byte int representing 130 into a one-byte variable sum thereby loosing its original value and assigning -126 instead:

     120             00000000 00000000 00000000 01111000
    + 10           + 00000000 00000000 00000000 00001010
    ----           -------------------------------------
     130             00000000 00000000 00000000 10000010
    
    -126               cast int to byte: -----> 10000010

Conclusion: a = a + 1 and a++ surprisingly differ in behaviour when being applied to non-int variables.

exercise No. 37

Adding values

Q:

Consider the following code:

System.out.println(2147483647 + 1);
System.out.println(2147483647 + 1L);

This yields:

-2147483648
2147483648

Explain this result. Please refrain from answering Thats what bankers do!

A:

The value 2147483647 is actually the largest possible int value Integer.MAX_VALUE. The plus operator in the first expression 2147483647 + 1 acts on two operands of type int. Thus the resulting sum will be of type int as well irrespective of its value.

On binary level adding an int value of 1 results in:

  01111111_11111111_11111111_11111111    2147483647
+ 00000000_00000000_00000000_00000001   +         1
-------------------------------------  ------------
  10000000_00000000_00000000_00000000   -2147483648

With respect to two-complement representation of signed int values this is actually an overflow error: There simply is no positive int value of 2147483648 in Java. See http://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.1.3-220 for details.

On contrary the plus operator in the expression 2147483647 + 1L acts on an int to its left and a long to its right. The result will thus be of type long:

                                      01111111_11111111_11111111_11111111    2147483647
+ 00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000001   +         1
-------------------------------------------------------------------------  ------------
  00000000_00000000_00000000_00000000_10000000_00000000_00000000_00000000    2147483648

Due to a long's larger allocation of eight rather than four bytes execution reveals no overflow problem thus yielding the expected result.

exercise No. 38

Representational float and double miracles

Q:

Consider and execute the following code snippet:

public static void main(String[] args) {
  final double a = 0.7;
  final double b = 0.9;
  final double x = a + 0.1; // 0.8
  final double y = b - 0.1; // 0.8
  System.out.println(x == y);
}

Which outcome do you expect? Explain the execution's result and propose a solution.

Tip

You will have to replace the == operator by something more appropriate addressing limited arithmetic precision.

A:

The expression x == y evaluates to false. This surprising result is due to limited precision regarding both float and double IEEE representations: A given value will be approximated as close as possible.

Adding System.out.println(x - y) yields a value of -1.1102230246251565E-16 denoting the representational deviation of x and y.

Comparing float and double values thus requires providing a representational distance limit below which two values will be regarded as equal:

final double a = 0.7;
final double b = 0.9;
final double x = a + 0.1;
final double y = b - 0.1;
System.out.println(Math.abs(x - y) < 1.E-14);

The last line represents the boolean expression | x - y | < 10 - 14 . So two values will be regarded as equal if their mutual distance is less than 0.00000000000001.

Figure 113. Detecting arithmetic overflow (Java 8+) Slide presentation
try {
    int sum = Math.addExact(2147480000, 2147480000);
    System.out.println("sum = " + sum);
} catch (ArithmeticException ex) {
    System.err.println("Problem: " + ex.getMessage());
}
Problem: integer overflow

Figure 114. Dividing by zero Slide presentation
double f = 34.3 / 0;
System.out.println("Value: " + f);
Value: Infinity

Watch out: Silent error!


exercise No. 39

Expressions involving infinity

Q:

Figure 114, “Dividing by zero ” raises some interesting questions:

  1. What happens if we add e.g. 1000 to a variable containing a value of Infinity?

  2. What happens if we add or subtract two variables both containing a value of Infinity?

A:

  • double f = 1. / 0;
    double difference = f - 1000;
    System.out.println("Difference: " + difference);
    Difference: Infinity
  • double f = 1. / 0;
    double g = 1. / 0;
    double difference = f - g;
    System.out.println("Difference: " + difference);
    Difference: NaN

    Explanation: This represents a special not a number encoded value.

Figure 115. Generic binary operator Slide presentation
Generic binary operator

Figure 116. The modulus operator % Slide presentation

Get a division's remainder:

int nuggets = 11,
    diggers = 3;

System.out.println("Nuggets per digger:" + nuggets / diggers);
System.out.println("Remaining nuggets:" + nuggets % diggers);
Nuggets per digger:3
Remaining nuggets:2

Figure 117. Binary operator type examples Slide presentation
Left Right Out Examples
boolean boolean boolean |, &, &&, ||, ^
int int int

+, -, *, /, % (read here!)

int long long
byte byte int
double float double
int float float
char byte int

The careful reader may stumble upon the absence of e.g. a binary «+» operator turning two byte values a and b into an expression a + b of type byte rather than int:

Figure 118. No binary + operator yielding byte Slide presentation
byte a = 1, b = 2;

byte sum = a + b; // Error: Incompatible types. 
                  // Required: byte
                  // Found: int

This is due to a Java Virtual Machine design decision leading to a limited set of computational types:

Given the Java Virtual Machine's one-byte opcode size, encoding types into opcodes places pressure on the design of its instruction set. If each typed instruction supported all of the Java Virtual Machine's run-time data types, there would be more instructions than could be represented in a byte. Instead, the instruction set of the Java Virtual Machine provides a reduced level of type support for certain operations. In other words, the instruction set is intentionally not orthogonal. Separate instructions can be used to convert between unsupported and supported data types as necessary.


exercise No. 40

int to short assignment

Q:

Consider the following code segment:

Figure 119. int expression assignment
short a = 4; 
short sum = a + 7; 

Declaring a short variable a assigning 4.

Declaring a short variable sum assigning 4 + 7. This yields a compile time error:

Type mismatch: cannot convert from int to short

On contrary the following statement compiles flawlessly:

Figure 120. Constant expression assignment
short sum = 4 + 7;

Explain this strange behaviour.

A:

Considering short sum = 4 + 7 the plus operator acts on two int operands. The expression 4 + 7 is thus of type int (not short). Both operands are static i.e. their values are known at compile time rather than at run time. The 4 + 7 expression's value can thus be evaluated at compile time. The resulting value of 11 is within [ - 2 15 , - 2 15 - 1 ] so the compiler will safely assign 11 to the variable sum of type short without any information loss.

The expression a + 7 contains a variable a of type short to the plus operator's left and an int literal 7 to its right. Thus the expression is of type int as well. However a compiler will consider a's value to be unknown at compile time (albeit humans will safely assume a value of 4 by virtue of the preceding statement). Therefore a + 7 could potentially exceed a short's range of values and thus cannot be assigned to the variable sum in a safe manner.

exercise No. 41

int to short assignment using final

Q:

We reconsider a variant of Figure 119, “int expression assignment”:

final short a = 4;
short sum = a + 7;

This time the code compiles flawlessly. Explain the underlying reason being related to the final modifier's role.

A:

A final variable's value cannot change. Thus the expression a + 7 is fully equivalent to 4 + 7 and can thus be evaluated at compile time. Like in the preceding Figure 120, “Constant expression assignment” code sample the resulting value of 11 neatly fits into the variable sum of type short.

exercise No. 42

Calculating a circle's area avoiding accidental redefinition

Q:

In Exercise Calculating a circle's area you calculated a given circle's area:

public static void main(String[] args) {

  double radius = 2.31;   // A circle having a radius (given e.g. in mm).
  double pi = 3.1415926;  // Constant relating a circle's radius, perimeter
                              //and area.

  double area = pi * radius * radius;
  System.out.println(area);
}

Though there is nothing wrong with this approach it is error prone: A careless programmer might accidentally redefine the value of pi:

double pi = 3.141592653589793;

double radius = 2.3; // Computing a circle's area
System.out.println("A circle of radius " + radius + " will cover an area of " +
  pi * radius * radius);

pi = -4; // Woops, accidential redefinition

radius = 1.8;

System.out.println("A circle of radius " + radius + " will cover an area of " +
   pi * radius * radius);

Modify the above code to avoid this type of error.

Tip

A:

The solution is straightforward. We just add a final modifier to the definition of our variable pi:

final  double pi = 3.141592653589793;
...
pi = -4; 
...

A final modifier prohibits subsequent variable assignments thus keeping its initially assigned value constant.

The assignment attempt is being flagged as a compile time error:

Cannot assign a value to final variable 'pi'

As a rule of thumb: Whenever you intend a variable not to change after an initial assignment use final declaring it to remain constant.

In addition refactoring our variable pi using capital letters reflecting the naming convention for constants. This is does not change program execution in any way but enhances code readability:

final double PI = 3.141592653589793;
double radius = 2.3; // Computing a circle's area
System.out.println("A circle of radius " + radius + " will cover an area of " +
  PI * radius * radius);

PI = -4; // Compile time error

radius = 1.8;

System.out.println("A circle of radius " + radius + " will cover an area of " +
   PI * radius * radius);

exercise No. 43

Turning weeks into seconds

Q:

Consider:

final short
  weeks = 2,
  days = 3,
  hours = 14,
  minutes = 32,
  seconds = 55;

final int secondsElapsed = ...; // TODO: assign elapsed seconds corresponding to previous time frame.

System.out.println(secondsElapsed);

Create an expression based on the variables weeks, days, hours, minutes and seconds to calculate the number of seconds elapsed for the given time period of two weeks, three days and so on.

After you have completed this task read about Horner's rule and try to improve your solution with respect to execution time.

A:

A straightforward solution reads:

final short
  weeks = 2,
  days = 3,
  hours = 14,
  minutes = 32,
  seconds = 55;

final int secondsElapsed =
  seconds + 60 * minutes + 60 * 60 * hours + 60 * 60 *  24 * days + 60 * 60 *  24 * 7 * weeks;

System.out.println(secondsElapsed);

This requires four additions and 10 multiplications. Horner's rule allows for:

...
final int secondsElapsed =
  seconds + 60 * (minutes + 60 * (hours + 24 * (days + 7 * weeks)));
...

This still leaves us with four additions but just 4 instead of 10 multiplications.

Within the given context the difference in execution time can be neglected. But in a larger application the calculation in question might have to be repeated millions of times giving rise to a substantial gain with respect to execution time.

exercise No. 44

Turning seconds into weeks

Q:

This exercise is about reversing Turning weeks into seconds namely decomposing a given value of elapsed seconds into weeks, days, hours, minutes and seconds:

final int secondsElapsed = 1521175;

final int weeks = ...; // TODO
final int days = ...;// TODO
final int hours = ...;// TODO
final int minutes = ...;// TODO
final int seconds = ...;// TODO

System.out.println(weeks + ", " + days + ", " + hours + ", " + minutes + ", " + seconds);

Tip

As an example consider decomposing 192 seconds into minutes and seconds:

  • Integer division 192 / 60 seconds yields 3 minutes.

  • We get the remaining seconds by virtue of the remainder operator 192 % 60.

Thus 192 seconds equal 3 minutes and 12 seconds

A:

We extend the given minute calculation example to all 5 desired values:

final int secondsElapsed = 1521175;

int remaining = secondsElapsed;

final int seconds = remaining % 60;
remaining /= 60;          // Minutes

final int minutes = remaining % 60;
remaining /= 60;          // hours

final int hours = remaining % 24;
remaining /= 24;          // Days

final int days = remaining % 7;
final int weeks = remaining / 7;

System.out.println(weeks + ", " + days + ", " + hours + ", " + minutes + ", " + seconds);

exercise No. 45

Using predefined Java standard library constants

Q:

In the previous exercise we coded:

final double PI = 3.141592653589793;
double radius = 2.3; // Computing a circle's area
System.out.println("A circle of radius " + radius + " will cover an area of " +
  PI * radius * radius);

Do we actually have to provide the value of pi (3.141592653589793) ourself?

Tip

Consider the standard Math library.

A:

The solution is straightforward using Math.PI:

double radius = 2.3; // Computing a circle's area
System.out.println("A circle of radius " + radius + " will cover an area of " +
  Math.PI * radius * radius);

exercise No. 46

Converting temperature values

Q:

Write an application converting temperature values being represented as degree centigrade to kelvin and Fahrenheit:

public static void main(String[] args) {

  double temperatureCelsius = 23.2;

      ...

  System.out.println("Celsius: " + temperatureCelsius);
  System.out.println("Kelvin: " + temperatureKelvin);
  System.out.println("Fahrenheit: " + temperatureFahrenheit);
}

A:

public static void main(String[] args) {

  double temperatureCelsius = 23.2;

  double
      temperatureKelvin = temperatureCelsius + 273.15,
      temperatureFahrenheit = 9 * temperatureCelsius / 5 + 32;

  System.out.println("Celsius: " + temperatureCelsius);
  System.out.println("Kelvin: " + temperatureKelvin);
  System.out.println("Fahrenheit: " + temperatureFahrenheit);
}

exercise No. 47

Time unit conversion

Q:

This is a two part exercise:

  1. Write an application converting a (seconds, minutes ,hours) time specification to seconds:

    public static void main(String[] args) {
    
      final int
          seconds = 31,
          minutes = 16,
          hours = 4;
    
      int timeInSeconds ...
    
      System.out.println("Time in seconds:" + timeInSeconds);
    }

    The expected output reads:

    Time in seconds:15391
  2. Reverse the previous part of this exercise: Convert a time specification in seconds to (seconds, minutes ,hours) as in:

    public static void main(String[] args) {
    
      final int timeInSeconds = 15391;
    
        ...
    
      System.out.println("Hours:" +  hours);
      System.out.println("Minutes:" +  minutes);
      System.out.println("Seconds:" +  seconds);
    }

    The expected output reads:

    Hours:4
    Minutes:16
    Seconds:31

    Tip

    Consider the remainder operator % (modulo operator).

A:

  1. A straightforward solution reads:

    public static void main(String[] args) {
    
      final int
        seconds = 31,
        minutes = 16,
        hours = 4;
    
      final int timeInSeconds = seconds + 60 * (minutes + 60 * hours);
    
      System.out.println("Time in seconds:" + timeInSeconds);
    }
  2. public static void main(String[] args) {
    
      final int timeInSeconds = 15391;
    
      final int minutesRemaining = timeInSeconds / 60;
      final int seconds = timeInSeconds % 60;
    
      final int hours = minutesRemaining / 60;
      final int minutes = minutesRemaining % 60;
    
      System.out.println("Hours:" +  hours);
      System.out.println("Minutes:" +  minutes);
      System.out.println("Seconds:" +  seconds);
    }

exercise No. 48

Interest calculation

Q:

We want to calculate the compounded interest starting from an initial capital, a given annual interest rate and a duration of three years. Consider the following code fragment:

public static void main(String[] args) {

  final double initialCapital = 223.12;
  final double interestRate = 1.5;

  // TODO ...

  System.out.println("Capital after three years:" + ...);
}

The expected output is:

Initial capital: 223.12€
Annual interest rate: 1.5%
Interest period: 3 years

Capital after three years:233.31175902999993

Extend the given code accordingly.

Tip

In case you are unsure read about calculating compounded interest.

In case you already have prior knowledge about loop statements in Java you may want to allow for a variable number of years:

// Interest parameters
final double initialCapital = 223.12;
final double interestRate = 1.5;
final int interestPeriodInYears = 3;
  ...
// Printing result
System.out.println("Capital after three years:" + finalCapital);

A:

We obtain the three year's compounded interest by:

finalCapital = initialCapital ( 1 + interestRate 100 ) 3

Since we have not yet introduced loops the multiplication has to be repeated three times ❶:

// Interest parameters
final double initialCapital = 223.12;
final double interestRate = 1.5;

// Printing start values
System.out.println("Initial capital: " + initialCapital + '€');
System.out.println("Annual interest rate: " + interestRate + '%');
System.out.println("Interest period: 3 years");

// Calculations
final double annualSurplusFactor = (1 + interestRate / 100);
double finalCapital = initialCapital;

finalCapital *= annualSurplusFactor; ❶
finalCapital *= annualSurplusFactor; ❶
finalCapital *= annualSurplusFactor; ❶

// Printing result
System.out.println("Capital after three years: " + finalCapital);

We might as well use a single arithmetic expression achieving the same result:

...
final double
  annualSurplusFactor = 1 + interestRate/100,
  finalCapital = initialCapital * annualSurplusFactor * annualSurplusFactor * annualSurplusFactor;
...

Using loop statements our solution becomes more flexible with respect to the interest period in question:

finalCapital = initialCapital ( 1 + interestRate 100 ) interestPeriodInYears
// Interest parameters
final double initialCapital = 223.12;
final double interestRate = 1.5;
final int interestPeriodInYears = 3;

// Printing start values
System.out.println("Initial capital: " + initialCapital + '€');
System.out.println("Annual interest rate: " + interestRate + '%');
System.out.println("Interest period: " + interestPeriodInYears +
                  (interestPeriodInYears == 1? " year": " years"));

// Calculations
final double annualSurplusFactor = (1 + interestRate / 100);
double finalCapital = initialCapital;

for (int i = 0; i < interestPeriodInYears; i++) {
   finalCapital *= annualSurplusFactor;
}

// Printing result
System.out.println("Capital after three years: " + finalCapital);

In the section called “Interest calculations” we will present an even more elaborate solution based on classes.

exercise No. 49

Summing short and char

Q:

Consider the following snippet:

short s = 1;
char c = 'A'; // ASCII 65
System.out.println(s + c);

Execution results in 66. What is the s+c expression's type?

Tip

Try to assign the expression to another variable and experiment using type candidates. Try to make an educated guess explaining the result.

A:

Both short and char require two bytes in Java. It is thus surprising that a mixed expression has got neither of both input types:

short s = 1;
char c = 'A';
short resultShort = s + c; // Incompatible types Required: short found: int
char resultChar= s + c;    // Incompatible types Required: char found: int

According to the compiler's error message the expression s + c is of type int. Why choosing a result type being twice the size of both input types?

The two input types have different ranges:

short

Signed integer value ranging from - 2 15 to 2 15 - 1 .

char

Unsigned integer value ranging from 0 to 2 16 - 1 .

A promotion to int guarantees correct results in all circumstances.

Figure 121. The logical and operator & Slide presentation

Boolean and of two operands:

boolean examSuccess = true,
        registered = false;

boolean examPassed = examSuccess & registered;

System.out.println("Exam passed:" + examPassed);
Exam passed:false

exercise No. 50

Operator & vs. &&

Q:

Execute the following snippet:

int points = 4;

boolean registered = false;

boolean examSuccess = registered & 4 <= points++;

System.out.println("Exam success:" + examSuccess + ", points = " + points);

Note the resulting output. Then replace the operator & by &&. Execute your code another time and explain your result.

Tip

Read 15.22.2 and 15.23 to understand the difference between & vs. && and | vs. ||.

A:

Execution yields:

Exam success:false, points = 5

On modifying our code by registered && 4 <= points++ execution now results in:

Exam success:false, points = 4

The operators & evaluates both its left and right hand side expressions unconditionally. The right hand side expression 4 <= points++ thereby increases our variable points by one.

The & operator's left hand operand already carries a false value. From a logical point of view the and result will thus be false irrespective of the operator's right hand side. With respect to the logical result evaluating 4 <= points++ is a waste of time.

The && operator allows for precisely this shortcut: If its left hand side operand is already false the right hand side will not be evaluated hence retaining our points variable's value of 4. See 15.23. Conditional-And Operator && for further reference.

Likewise the || operator will only evaluate its right hand side if its left evaluates to false.