Let's take a look at the result of casting this large double to each of the other numeric primitive types:
double d = 4786777867867868654674678346734763478673478654478967.77;
System.out.printf("float %f\n", (float)d);
System.out.printf("long %d\n", (long)d);
System.out.printf("int %d\n", (int)d);
System.out.printf("short %d\n", (short)d);
System.out.printf("byte %d\n", (byte)d);
Output:
float Infinity
long 9223372036854775807
int 2147483647
short -1
byte -1
Float
From the JLS:
A narrowing primitive conversion from double to float is governed by
the IEEE 754 rounding rules (§4.2.4). This conversion can lose
precision, but also lose range, resulting in a float zero from a
nonzero double and a float infinity from a finite double.
Basically, IEEE 754 mandates this behaviour. IEEE 754 sets aside a specific bit pattern to represent infinity, and all floating-point operations involving this value are defined.
Long & Int
The JLS states that in the case of a narrowing primitive conversion from double to long or int, where the value is too large to fit in the range (64 or 32 bit signed integer), the largest representable value should be used, Long.MAX_VALUE and Integer.MAX_VALUE;
Short & Byte
In casting a double to a short or byte, the number is first cast to an int, using the rule above. It is then cast from int to short or byte by discarding all but the n lowest bits (16 for short, 8 for byte). Since all but the high bit of Integer.MAX_VALUE are set to 1, the short and byte values have all bits set, which corresponds to -1 in signed two's complement.