The following 23 specific conversions on primitive types are called the narrowing primitive conversions:
byte
to char
short
to byte
or char
char
to byte
or short
int
to byte
, short
, or char
long
to byte
, short
, char
, or int
float
to byte
, short
, char
, int
, or long
double
to byte
, short
, char
, int
, long
, or float
Narrowing conversions may lose information about the overall magnitude of a numeric value and may also lose precision.
A narrowing conversion of a signed integer to an integral type T simply discards all but the n lowest order bits, where n is the number of bits used to represent type T. In addition to a possible loss of information about the magnitude of the numeric value, this may cause the sign of the resulting value to differ from the sign of the input value.
A narrowing conversion of a character to an integral type T likewise simply discards all but the n lowest order bits, where n is the number of bits used to represent type T. In addition to a possible loss of information about the magnitude of the numeric value, this may cause the resulting value to be a negative number, even though characters represent 16-bit unsigned integer values.
A narrowing conversion of a floating-point number to an integral type T takes two steps:
long
, if T is long
, or to an int
, if T is byte
, short
, char
, or int
, as follows:
int
or long
0
.
long
, and this integer value can be represented as a long
, then the result of the first step is the long
value V.
int
, then the result of the first step is the int
value V.
int
or long
.
int
or long
.
int
or long
, the result of the conversion is the result of the first step.
byte
, char
, or short
, the result of the conversion is the result of a narrowing conversion to type T (§5.1.3) of the result of the first step.
The example:
class Test { public static void main(String[] args) { float fmin = Float.NEGATIVE_INFINITY; float fmax = Float.POSITIVE_INFINITY; System.out.println("long: " + (long)fmin + ".." + (long)fmax); System.out.println("int: " + (int)fmin + ".." + (int)fmax); System.out.println("short: " + (short)fmin + ".." + (short)fmax); System.out.println("char: " + (int)(char)fmin + ".." + (int)(char)fmax); System.out.println("byte: " + (byte)fmin + ".." + (byte)fmax); } }
long: -9223372036854775808..9223372036854775807 int: -2147483648..2147483647 short: 0..-1 char: 0..65535 byte: 0..-1
The results for char
, int
, and long
are unsurprising, producing the minimum and maximum representable values of the type.
The results for byte
and short
lose information about the sign and magnitude of the numeric values and also lose precision. The results can be understood by examining the low order bits of the minimum and maximum int.
The minimum int
is, in hexadecimal, 0x80000000
, and the maximum int
is 0x7fffffff
. This explains the short
results, which are the low 16 bits of these values, namely, 0x0000
and 0xffff
; it explains the char
results, which also are the low 16 bits of these values, namely, '\u0000'
and '\uffff'
; and it explains the byte
results, which are the low 8 bits of these values, namely, 0x00
and 0xff
.
A narrowing conversion from double
to float
behaves in accordance with IEEE 754. The result is correctly rounded using IEEE 754 round-to-nearest mode. A value too small to be represented as a float
is converted to positive or negative zero; a value too large to be represented as a float
is converted to a (positive or negative) infinity. A double
NaN is always converted to a float
NaN.
Despite the fact that overflow, underflow, or other loss of information may occur, narrowing conversions among primitive types never result in a run-time exception (§11).
Here is a small test program that demonstrates a number of narrowing conversions that lose information:
class Test { public static void main(String[] args) { // A narrowing of int to short loses high bits: System.out.println("(short)0x12345678==0x" + Integer.toHexString((short)0x12345678));
// A int value not fitting in byte changes sign and magnitude: System.out.println("(byte)255==" + (byte)255);
// A float value too big to fit gives largest int value: System.out.println("(int)1e20f==" + (int)1e20f);
// A NaN converted to int yields zero: System.out.println("(int)NaN==" + (int)Float.NaN);
// A double value too large for float yields infinity: System.out.println("(float)-1e100==" + (float)-1e100);
// A double value too small for float underflows to zero: System.out.println("(float)1e-50==" + (float)1e-50);
}
}
This test program produces the following output:
(short)0x12345678==0x5678 (byte)255==-1 (int)1e20f==2147483647 (int)NaN==0 (float)-1e100==-Infinity (float)1e-50==0.0