Why does Java implicitly (without cast) convert a `long` to a `float`?
The same question could be asked of long
to double
- both conversions may lose information.
Section 5.1.2 of the Java Language Specification says:
Widening primitive conversions do not
lose information about the overall
magnitude of a numeric value. Indeed,
conversions widening from an integral
type to another integral type do not
lose any information at all; the
numeric value is preserved exactly.
Conversions widening from float to
double in strictfp expressions also
preserve the numeric value exactly;
however, such conversions that are not
strictfp may lose information about
the overall magnitude of the converted
value.Conversion of an int or a long value
to float, or of a long value 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).
In other words even though you may lose information, you know that the value will still be in the overall range of the target type.
The choice could certainly have been made to require all implicit conversions to lose no information at all - so int
and long
to float
would have been explicit and long
to double
would have been explicit. (int
to double
is okay; a double
has enough precision to accurately represent all int
values.)
In some cases that would have been useful - in some cases not. Language design is about compromise; you can't win 'em all. I'm not sure what decision I'd have made...
Why can you store a long into a float without typecast
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
toshort
,int
,long
,float
, ordouble
short
toint
,long
,float
, ordouble
char
toint
,long
,float
, ordouble
int
tolong
,float
, ordouble
long
tofloat
ordouble
...
A widening primitive conversion from
int
tofloat
, or fromlong
tofloat
, 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.
Why long literal can be assigned to float variable while float datatype uses less memory?
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
int
tofloat
, or fromlong
tofloat
, or fromlong
todouble
, 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).
Explicit casting a long into an int and implicit casting a long to a float
Long.MAX_VALUE = (2^63)-1
Float.MAX_VALUE= 2^127
So, Long value always fits into Float value.
Compiler doesn't analyse real values from your code. It never verifies if the value may fit.
Why can int/byte/short/long be converted to float/double without typecasting but vice-versa not possible
The reason is specified in JLS 5.1.2:
It says that : long to float or double is a widening conversion.
Whereas, float to byte, short, char, int, or long is narrowing conversion.
That's why float b=a;
is working fine in your program , since it is widening conversion.
And long c=b;
is showing compilation error since it is a narrowing conversion.
typecasting long to float implicitly not narrowing
It's not about how many bits of data are used. It's about the scale that can be represented.
From JLS section 5.1.2 (widening primitive conversions):
A widening primitive conversion does not lose information about the overall magnitude of a numeric value.
...
A widening conversion of an int or a long value to float, or of a long value 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).
The range of long
is much smaller than the range of float
, but with a fixed precision of 1. The precision of float
varies across the range, in terms of absolute value. In other words, no long
is outside the range of float
, but there are long
values which can't be precisely represented as float
values.
Moving to double
wouldn't help this - there are long
values which can't be precisely represented as double
, either.
Why can a float be an integer without throwing an exception?
24.0
is a double
literal, and cannot be automatically (implicitly) converted to a float
. 24
is an int
literal, so what you see here is a widening primitive conversion from an int
to a float
.
Java allows implicit conversion of int to float. Why?
You are probably wondering:
Why is this an implicit conversion when there is a loss of information? Shouldn't this be an explicit conversion?
And you of course have a good point. But the language designers decided that if the target type has a range large enough then an implicit conversion is allowed, even though there may be a loss of precision. Note that it is the range that is important, not the precision. A float has a greater range than an int, so it is an implicit conversion.
The Java specification says the following:
A widening conversion of an int or a long value to float, or of a long value 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.
Related Topics
What Is the Point of Heterogenous Arrays
How to Use JavaScript with Selenium Webdriver Java
Mousemotionlistener in Java Swing, Using It with Components Inside Components etc
What Is the Main Difference Between Inheritance and Polymorphism
How to Convert an Array to a Set in Java
Hibernate: Different Object with the Same Identifier Value Was Already Associated with the Session
Compile Code Using Javafx 2.0 (Using Command Line)
Javax.Swing Timer Repeats Fine, But Actionlistener Doesn't Do Anything
How to Switch JPAnels Inside a Jframe
Java Heap Terminology: Young, Old and Permanent Generations
Nullpointerexception in Java with No Stacktrace
Difference Between _Java_Options, Java_Tool_Options and Java_Opts
How to Move My Jmenubar to the Screen Menu Bar on MAC Os X
Volatile Boolean VS Atomicboolean
Different Dependencies for Different Build Profiles