Simple calculations

exercise No. 5

Working with variables

Q:

This exercise is about two variables a and b representing values being subject to change. Embed the following snippet in a class and execute the beast:

public static void main(String[] args) {

  int a = 4,
      b = 7;

  System.out.println("a=" + a);
  System.out.println("b=" + b);

  int sum = a + b;
  System.out.println("Sum: " + sum);
}
  1. Modify these values and code more sophisticated expressions.

  2. Replace all three System.out.println(...) statements into a single one:

    Source code Expected output
    int a = 4,
        b = 7;
    
    System.out.println(...); // TODO
    4+7=11

    Your code is expected to produce correct output when changing the values of the two variables a and b:

    Source code Expected output
    int a = -4,  // Changing just values
        b = 100;
    
    System.out.println(...); // Remains unchanged
    -4+100=96

    Tip

    Java does not only provide an arithmetic + operator: You have already seen the + operator connecting two strings. Java extends this concept of concatenation to non-string values as well.

A:

A naive solution reads:

int a = -4,
    b = 100;

System.out.println(a + "+" + b + "=" + a + b);

This unfortunately does not work. In ordinary math e.g. 2 + 3 * 4 is being evaluated as 2 + (3 * 4) rather than (2 + 3) * 4. Likewise the above expression is fully equivalent to:

System.out.println(((((a + "+") + b) + "=") + a) + b);

As we shall see in the section called “Operators and expressions” each of these constituents evaluates to a string thus producing:

-4+100=-4100

A better solution thus forcing an arithmetic + operation requires a pair of braces grouping (a+b):

Source code Output
int a = -4,
    b = 100;

System.out.println(a + "+" + b+ "=" + (a + b));
-4+100=96

Reverting to the original variable values 4 and 7 still yields a correct output:

Source code Output
int a = 4,
    b = 7;

System.out.println(a + "+" + b+ "=" + (a + b));
4+7=11

exercise No. 6

Code equivalence

Q:

The solution of Working with variables claims full source code equivalence between System.out.println(a + "+" + b + "=" + a + b) and System.out.println(((((a + "+") + b) + "=") + a) + b). Is there a way to prove this statement?

Tip

Think about the Java compile and execution process. What exactly does equivalence refer to? You may also want to read the diff command's documentation.

A:

We start from the following two source code versions:

Version 1 Version 2
public class HelloWorld {

  public static void main(String[] args) {
    int a = 4,
        b = 7;

    System.out.println(a + "+" + b+ "=" + a + b);
  }
}
public class HelloWorld {

  public static void main(String[] args) {
    int a = 4,
        b = 7;

   System.out.println(((((a + "+") + b) + "=") + a) + b);
  }
}

Source code equivalence means equivalence of executing the generated bytecode. The easiest case happens if compiling both versions yields identical bytecode. This is indeed true and can be proven by:

  1. Compile source code version 1 by executing javac HelloWorld.java.

  2. Rename the resulting bytecode file HelloWorld.class to HelloWorld.1.class.

  3. Repeat the previous steps for source code version 2 resulting in HelloWorld.2.class.

  4. Compare the two files HelloWorld.1.class and HelloWorld.2.class. Result:

    diff HelloWorld.1.class HelloWorld.2.class

The diff command returning no result states the the two files to be identical. Otherwise we would see e.g.:

diff HelloWorld.class HelloWorld.2.class
Binary files HelloWorld.class and HelloWorld.2.class differ

exercise No. 7

Different byte code, same execution results

Q:

Can you imagine two different byte code files being fully equivalent with respect to execution?

A:

There are various scenario for this to happen:

  1. Two source code versions may already be equivalent albeit syntactically different. Consider the following example differing only by the definition order of two variables a and b:

    Version 1 Version 2
    public class HelloWorld {
      public static void main(String[] args) {
        int a = 4;
        int b = 7;
    
        System.out.println(a + b);
      }
    }
    public class HelloWorld {
      public static void main(String[] args) {
        int b = 7;
        int a = 4;
    
       System.out.println(a + b);
      }
    }

    Obviously these two versions are equivalent with respect to execution. However the derived byte code differs:

    >diff HelloWorld.class.1 HelloWorld.class.2
    Binary files HelloWorld.class.1 and HelloWorld.class.2 differ
  2. The javac compiler allows for additional options. You may add e.g. javac -g HelloWorld.java. This adds so called debugging information to the generated byte code causing the latter to differ.

  3. javac compilers from different vendors (Oracle, IBM, OpenJDK) or versions (1.7, 1.8, 1.9, 10, 11 and so on) will most likely produce different byte code hopefully executing identically.