float uses 32 bits and long uses 64 bits. Why is this allowed by the compiler then?
float x = 5555555555555555555L;
The range of float is broader than the range of long. So while you can certainly lose precision with an assignment like this (and can when assigning a long value to double too), the general magnitude is already representable - so there's never be a need to throw an exception due to it being out of range, for example.
The JLS (section 5.1.2) says this:
A widening primitive conversion from
inttofloat, or fromlongtofloat, or fromlongtodouble, may result in loss of precision - that is, the result may lose some of the least significant bits of the value. In this case, the resulting floating-point value will be a correctly rounded version of the integer value, using IEEE 754 round-to-nearest mode (ยง4.2.4).