Arithmetic and logical operators

Figure 156. The binary plus operator Slide presentation Create comment in forum
The binary plus operator

Figure 157. Binary operator output type Slide presentation Create comment in forum
Binary operator output type

exercise No. 36

Calculating a circle's area Create comment in forum

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. 37

Dividing values Create comment in forum

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. 38

Strange things happen Create comment in forum

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 following logically 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. Thus incrementing 127 by 1 yields -128 and thus an overflow error.

    Since Java uses Two's complement representation we have:

    01111111 127
    +00000001 +1
    =10000000 =-128

    On machine level the above calculation is just an ordinary addition.

    Conclusion: Watch out when doing (integer) arithmetic!

  2. The compile time error is due to the definition of the binary + operator: In Java it'll always returning an int rather than a byte 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. 39

Adding values Create comment in forum

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. 40

Representational float and double miracles Create comment in forum

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 158. Detecting arithmetic overflow (Java 8+) Slide presentation Create comment in forum
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 159. Dividing by zero Slide presentation Create comment in forum
double f = 34.3 / 0;
System.out.println("Value: " + f);
Value: Infinity

Watch out: Silent error!


exercise No. 41

Expressions involving infinity Create comment in forum

Q:

Figure 159, “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 160. Operator type behaviour Slide presentation Create comment in forum
Operator type behaviour

Figure 161. The modulus operator % Slide presentation Create comment in forum

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 162. Binary operator type examples Slide presentation Create comment in forum
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 163. No binary + operator yielding byte Slide presentation Create comment in forum
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. 42

int to short assignment Create comment in forum

Q:

Consider the following code segment:

Figure 164. 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 165. 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 long). 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. 43

int to short assignment using final Create comment in forum

Q:

We reconsider a variant of Figure 164, “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 165, “Constant expression assignment” code sample the resulting value of 11 neatly fits into the variable sum of type short.

exercise No. 44

Calculating a circle's area avoiding accidental redefinition Create comment in forum

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. 45

Turning weeks into seconds Create comment in forum

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. 46

Turning seconds into weeks Create comment in forum

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. For the sake of convenience the number of seconds per minute, per hour and so on are being calculated beforehand:

final int secondsElapsed = 1521175;

// Defining helpful constants
//
final int
  secondsPerMinute = 60,                    // 60 minutes per hour
  secondsPerHour =   60 * secondsPerMinute, // 3600 seconds per hour
  secondsPerDay =    24 * secondsPerHour,   // 86400 seconds per day
  secondsPerWeek =    7 * secondsPerDay;    // 604800 seconds per week

int remainingSeconds = secondsElapsed;

final int weeks = remainingSeconds / secondsPerWeek;
remainingSeconds = remainingSeconds % secondsPerWeek;

final int days = remainingSeconds / secondsPerDay;
remainingSeconds %= secondsPerDay;

final int hours = remainingSeconds / secondsPerHour;
remainingSeconds %= secondsPerHour;

final int minutes = remainingSeconds / secondsPerMinute;
remainingSeconds %= secondsPerMinute;

final int seconds = remainingSeconds;

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

exercise No. 47

Using predefined Java standard library constants Create comment in forum

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. 48

Converting temperature values Create comment in forum

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. 49

Time unit conversion Create comment in forum

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. 50

Interest calculation Create comment in forum

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;

  System.out.println("Initial capital:" + initialCapital);
  System.out.println("Annual interest rate:" + interestRate);

  // TODO ...

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

The expected output is:

Initial capital:223.12
Annual interest rate:1.5
Capital after three years:233.31175902999993

Tip

In case you are unsure read about calculating the compounded interest.

A:

We obtain the compounded interest by multiplying the initial capital by 1 + r / 100 for each year where r represents the given interest rate.

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

public static void main(String[] args) {

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

  System.out.println("Initial capital:" + initialCapital);
  System.out.println("Annual interest rate:" + interestRate);

  final double factor = 1. + interestRate/100.;
  double capitalAtThreeYears = initialCapital;

  capitalAtThreeYears *= factor; // Year 1
  capitalAtThreeYears *= factor; // Year 2
  capitalAtThreeYears *= factor; // Year 3

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

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

public static void main(String[] args) {

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

  System.out.println("Initial capital:" + initialCapital);
  System.out.println("Annual interest rate:" + interestRate);

  final double factor = 1. + interestRate/100.;
  final double capitalAtThreeYears =
        initialCapital * factor * factor * factor;

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

In the section called “Interest calculations” we will present a more elaborate solution based on loops and class methods.

exercise No. 51

Summing short and char Create comment in forum

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 166. The logical and operator & Slide presentation Create comment in forum

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. 52

Operator & vs. && Create comment in forum

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. 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.