These are examples of the Narrowing Primitive Conversion operation.
In your first example, long to int:
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.
So your (int) 2147483648l is taking the 64 bits of the long:
00000000 00000000 00000000 00000000 10000000 00000000 00000000 00000000
...and dropping the top 32 bits entirely:
10000000 00000000 00000000 00000000
...and taking the remaining 32 bits as an int. Since the leftmost of those is now a sign bit (long and int are stored as two's complement), and since it happens to be set in your 2147483648l value, you end up with a negative number. Since no other bits are set, in two's complement, that means you have the lowest negative number int can represent: -2147483648.
The float to int example follows a more complex rule. The relevant parts for your value are:
...if the floating-point number is not an infinity, the floating-point value is rounded to an integer value V, rounding toward zero using IEEE 754 round-toward-zero mode (§4.2.3).
...[if] the value [is] too large (a positive value of large magnitude or positive infinity), [then] the result of the first step is the largest representable value of type int or long.
(But see the part of the spec linked above for the details.)
So since 2147483648f rounds to 2147483648, and 2147483648 is too large to fit in int, the largest value for int (2147483647) is used instead.
So in the long to int, it's bit fiddling; in the float to int, it's more mathematical.
In a comment you've asked:
Do you know why both (short) 32768 and (short) 32768f evaluate to -32768? I was exepecting the latter to evaluate to 32767.
Excellent question, and that's where my "see the part of the spec linked above for the details" above comes in. :-) (short) 32768f does, in effect, (short)(int)32768f:
In the spec section linked above, under "A narrowing conversion of a floating-point number to an integral type T takes two steps:", it says
- In the first step, the floating-point number is converted either to a
long, if T is long, or to an int, if T is byte, short, char, or int...
and then later in Step 2's second bullet:
- * If T is
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.
So in step one, 32768f becomes 32768 (an int value), and then of course (short)32768 does the bit-chopping we saw in long => int above, giving us a short value of -32768.