Conversions

Figure 143. Widening from byte to short Slide presentation Create comment in forum
Widening from byte to short

Figure 144. Narrowing from int to char Slide presentation Create comment in forum
Narrowing from int to char

exercise No. 31

int to char narrowing problems Create comment in forum

Q:

Reconsidering Figure 144, “Narrowing from int to char we observe the following two related snippets yielding compile time errors

  1. int i = 65;
    char c = i;

    On contrary the following code compiles well:

    char c = 65;
  2. char c = 66200;

    On contrary the following code compiles well:

    char c = 64200;

Explain these errors and their underlying reasons and provide a solution if possible.

Tip

Which data types are involved? Think about narrowing conversions and type casting.

A:

  • Assigning an int to a char variable effectively narrows from four to two bytes and is thus prohibited. A fix requires an explicit type cast:

    int i = 65;
    char c = (char) i;

    Since 65 fits well into a char being limited by 2 16 - 1 our cast will not have any negative impact.

  • We consider the binary representations of 66200 and 64200:

    System.out.println(Integer.toBinaryString(66200)); // Yields 1_00000010_10011000
    System.out.println(Integer.toBinaryString(64200)); // Yields   11111010_11001000

    Thus 64200 fits into a two byte char whereas 66200 being larger than 2 16 - 1 does not.

exercise No. 32

Get a byte from 139 Create comment in forum

Q:

Consider:

int i = 139;
byte b = (byte) i;
System.out.println(b);

Explain in detail why execution results in a value of -117.

A:

A four byte int representation of 139 reads 00000000_00000000_00000000_10001011. The cast b = (byte) i will strip the leading three bytes leaving us with b containing 10001011.

Since byte values in Java are being represented as signed values in two-complement notation this equals decimal -117.

exercise No. 33

Ariane, I miss you! Create comment in forum

Q:

Reconsidering the Ariane 5 maiden flight crash read the comment buried in the solution of Inventing tinyint. . Try to mimic a code portion in Java showing the catastrophic error.

Start with a double variable using a value being suitable to be assigned to a short variable using a cast (narrowing).

Then in a second step raise this value breaking your short variable's upper limit.

A:

We start from:

double level = 2331.12; // smaller than 32767 == Short.MAX_VALUE

short value = (short) level;

System.out.println(value);

Execution yields an expected integer output of 2331. However increasing our level variable's value from 2331.12 to 42331.12 yields an output of -23205 due to an overflow.

exercise No. 34

Reducing long to int (difficult) Create comment in forum

Q:

For changing a map's scale from fine to coarse Joe programmer intends to map positive long values to int values. This requires scaling down half the long data type's range [ 0 , 2 63 - 1 ] to the int's range of [ 0 , 2 31 - 1 ] :

From To
long remark int remark
0 0
1 0
... 0
4294967295 2 32 - 1 0
4294967296 2 32 1
4294967297 2 32 + 1 1
...
9223372036854775806 2 63 - 2 or Long.MAX_VALUE - 1 2147483647 2 31 - 1 or Integer.MAX_VALUE
9223372036854775807 2 63 - 1 or Long.MAX_VALUE 2147483647 2 31 - 1 or Integer.MAX_VALUE

Joe's idea is dividing long values by 2 32 . As a result a long value of - 2 63 will be reduced to the intended value of - 2 31 . Since 2 32 seems to be equal to 2 * (Integer.MAX_VALUE + 1)) (why?) Joe's first attempt reads:

final long longValue = 2147483648L;
final int reducedValue = (int) (longValue / (2 * (Integer.MAX_VALUE + 1)));
System.out.println(reducedValue);

Unfortunately the results are not promising. This code merely results in a runtime error:

/usr/lib/jvm/java-8-oracle/bin/java ...
Exception in thread "main" java.lang.ArithmeticException: / by zero
	at qq.App.main(App.java:27)

Process finished with exit code 1

Explain the underlying problem and correct Joe's error.

Tip

It may be helpful thinking of a smaller example before. Consider two hypothetic signed integer types TinyLong and TinyInt of four and two bits respectively. The corresponding mapping will be:

TinyLong, n = 4 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7
TinyInt, n = 2 -2 -1 0 1

A:

Joe's intention with respect to our toy example types implies dividing TinyLong values by 2 2 (truncating). This indeed yields the desired result for non-negative values. So why does Joe encounter a division by zero runtime exception when executing longValue / (2 * (Integer.MAX_VALUE + 1))?

Unfortunately Joe's implementation is seriously flawed for even two reasons:

  1. The constant Integer.MAX_VALUE already suggests we will not be able to increase its value while staying as an int. The expression Integer.MAX_VALUE + 1 will be evaluated using int rather than long arithmetic returning:

      01111111_11111111_11111111_11111111
    + 00000000_00000000_00000000_00000001
    _____________________________________
      10000000_00000000_00000000_00000000

    This is the binary representation of the unintended result Integer.MIN_VALUE due to an arithmetic overflow. The expression 2 * (Integer.MAX_VALUE + 1) then gives rise to a second overflow error:

      10000000_00000000_00000000_00000000
    + 10000000_00000000_00000000_00000000
    _____________________________________
      00000000_00000000_00000000_00000000

Both errors combined surprisingly result in a value of 0 explaining the division by zero error message. There are two possible solutions:

(int) (longValue / (2L * (Integer.MAX_VALUE + 1L)))

Introducing 2L or 1L (one is sufficient) in favour of simply using 2 and 1 turns both addition and multiplication into operations involving at least one long argument. Thus for both operations the Java runtime will use long arithmetic returning the desired reducing factor of 2 32 of type long.

(int) (longValue / 2 / (Integer.MAX_VALUE + 1L))

Same result as before.

Note

This time the expression starts with longValue / 2 ... Since the variable longValue is of type long the expression longValue / 2 will be evaluated by the Java runtime using long arithmetics. The result will subsequently be divided by Integer.MAX_VALUE + 1L again using long arithmetic.