## Conversions

No. 28

### int and char

Q:

Explain the following output:

Code Output
char a = 0;
a -= 1;
int i = a;
System.out.println(i);
65535

Why do we see a result of 65535 rather than -1?

### Tip

Read about the a char's value range.

A:

In contrast to byte, short and int a two byte char represents no negative but only positive values and zero:

decimal binary
0 0000 0000 0000 0000
1 0000 0000 0000 0001
2 0000 0000 0000 0010
...
65535 ( $2 16 - 1$) 1111 1111 1111 1111

Like with other integer types the above table behaves in a cyclic way with respect to additions at its top and and subtractions at its bottom:

Code Output
char a = 65535;
a += 1;
int i = a;
System.out.println(i);
0

On machine level this may be conceived as an (incomplete) subtract operation:

 0000 0000 0000 0000
-0000 0000 0000 0001
--------------------
1111 1111 1111 1111



No. 29

### float vs. double

Q:

We consider:

Code Output
System.out.println( 3.14 );
System.out.println( 3.14f );
System.out.println( 3.14d );
3.14
3.14
3.14

There seems to be no difference between the three literals 3.14, 3.14f and 3.14d. Is this actually true?

### Tip

Read the section about floating point literals in Java. Write code exhibiting possible differences.

A:

The System.out.println( 3.14f ) statement's output is actually truncated with respect to output precision. Forcing 16 fractional digits to become visible reads:

Code Output
System.out.println(new DecimalFormat(
"#0.0000000000000000").format(3.14f));
3.1400001049041750

The value 3.1400001049041750 is the closest possible approximation to 3.14 when using a 4-byte IEEE float. A 3.14f double literal won't be exact either but doubles the number of representing bytes to 8 thereby enhancing its representational precision substantially. This causes unexpected results:

Code Output
System.out.println(3.14f - 3.14d);
1.0490417468034252E-7

This difference is a result of 3.14d providing four additional bytes of precision therefore matching the exact value of $314 100$ better than 3.14f.

Regarding types we have:

Literal Comment
3.14f 4-byte float literal
3.14d 8-byte double literal
3.14 Equivalent to 3.14d

Assignments to variables of type double are thus always guaranteed to work:

Code Comment
double d = 3.14f;
o.K., assigning float to double (widening)
double d = 3.14d;
o.K., assigning double to double
double d = 3.14;
o.K., assigning double to double as well.

Assignments to variables of type float may fail:

Code Comment
float f = 3.14f;
o.K., assigning float to float
float f = 3.14d;
Error, assigning double to float
float f = 3.14;
Error, assigning (implicit) double to float as well.

No. 30

### int to char narrowing problems

Q:

Reconsidering Figure 108, “Narrowing from int literal to char variable ” 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.

No. 31

### Get a byte from 139

 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.

No. 32

### Ariane, I miss you!

 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.

No. 33

### Reducing long to int (difficult)

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.