Conversions

Figure 112. Widening from byte to short Slide presentation

Figure 113. Narrowing from int literal to char variable Slide presentation

Figure 114. A widening «ladder» Slide presentation
byte   b = 42;  // Narrowing: constant int literal to byte
short  s = b;   // Widening
int    i = s;   // Widening
long   l = i;   // Widening
float  f = l;   // Widening
double d = f;   // Widening

Figure 115. A narrowing «ladder» Slide presentation
double d = 14.23;
float  f  = (float) d;  // Narrowing
long   l  = (long)  f;  // Narrowing
int    i  = (int)   l;  // Narrowing
short  s  = (short) i;  // Narrowing
byte   b  = (byte)  s;  // Narrowing

exercise No. 31

int and char

Q:

Explain the following output:

Code Result
char a = 0;
a -= 1;
int i = a;
IO.println(i);
65535

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

Tip

Reconsider a char's range of values.

A:

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

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

Like with other integer types values behave in a cyclic way with respect to both additions at its top and subtractions at its bottom:

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

On machine level this may be conceived as an (underflow) subtract operation. The beginning 1 and 0 are actually not present an a two-byte char but help understanding the underflow:

 10000 0000 0000 0000
- 0000 0000 0000 0001
—————————————————————
 01111 1111 1111 1111

exercise No. 32

float vs. double

Q:

We consider:

Code Output
IO.println( 3.14 );
IO.println( 3.14f );
IO.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.

Use e.g. System.out.format("%.16f\n", floatOrDoubleValue); for printing values with 16 digits precision.

A:

The IO.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.format("%.16f\n", 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
IO.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.

exercise No. 33

int to char narrowing problems

Q:

Reconsidering Figure 113, “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 compile time checks.

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:

    IO.println(Integer.toBinaryString(66200)); // Yields 1_00000010_10011000
    IO.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. 34

Get a byte from 139

Q:

Consider:

int i = 139;
byte b = (byte) i;
IO.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 eight bit two-complement notation this equals decimal -117.