Why can you store a long into a float without typecast

gmanrocks :

Ok so I know float is 32 bit decimal. Double is 64 bit decimal. Long is 64 bit whole number. So why is this value: Float(32 bit) <- Long (64 bit). Obviously not java code but I'm referring to it in java. It automagically casts even though it crunches it into 32 bits and loses precision. So that means precision does not matter. However int <- double does not work unless you explicitly cast the double to an int. It is a smaller size and they are both numbers and both primitives. Is it just the general rule: Decimal Number <- Whole number regardless of bit size. It will just loop back around. Just that rule and everything works among Numbers in java? To show actual code:

long L = 10_000L;
float f = L;

Note that the question is specifically about long to float without an explicit cast, not float to long, not about explicit casting.

T.J. Crowder :

Converting long to float is a widening primitive conversion. Java allows it without error because...it's defined that way. From the link:

19 specific conversions on primitive types are called the widening primitive conversions:

  • byte to short, int, long, float, or double

  • short to int, long, float, or double

  • char to int, long, float, or double

  • int to long, float, or double

  • long to float or double

...

A widening primitive conversion from int to float, or from long to float, or from long to double, 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).

(My emphasis.)

Why allow it without requiring an explicit cast? In general, only James Gosling and others there at the time can answer that sort of question. For the rest of us, the answer is: Because that's what the specification says.

However, I'll note that even though precision is lost, overall magnitude is not, which I'd wager is why it's allowed. float can hold very large values imprecisely. So for instance:

class Demo {
    public static void main(String[] args) {
        long l = Long.MAX_VALUE;
        float f = l;
        System.out.println(l);
        System.out.println(f);
    }
}

Run that and you get:

9223372036854775807
9.223372E18

9.223372e18 is 9223372000000000000. Compare:

9223372036854775807 - the long value
9223372000000000000 - the float value

This is because float can sacrifice precision for range of value, because it stores a base value that it raises to an exponent. From Wikipedia's article on the IEEE-754 binary32 format (which is what Java's float is):

...an IEEE 754 32-bit base-2 floating-point variable has a maximum value of (2 − 2−23) × 2127 ≈ 3.402823 × 1038...

3.402823 × 1038 is:

340,282,300,000,000,000,000,000,000,000,000,000,000

...but the highest integer it can store such that you can add 1 and not lose precision is just 224-1 (16,777,215). It can store 16,777,216, but not 16,777,217. So 16,777,216 + 1 is still 16,777,216:

class Demo {
    public static void main(String[] args) {
        float f = 16_777_216f;
        System.out.println(String.format("%8.0f", f));     // 16777216
        System.out.println(String.format("%8.0f", f + 1)); // 16777216 (still)
    }
}

That's because it's now storing the base value divided by 2 with an exponent of 2, so it can't store odd numbers anymore. It gets worse the higher you get, being able to only store multiples of 4, then of 8, then of 16, etc.

Contrast with the maxiumum value of a long, which is 263-1, which is "only":

9,223,372,036,854,775,807

But unlike float, it can represent each and every one of those integers precisely.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=97123&siteId=1