In the book of SCJP guide by Kathy Sierra, in the assignments chapter, we learn that we can declare something like this byte b = 7;. Behind the scene the code is byte b = (byte) 7;. This is so because in java, number 7 is considered a literal int value so has be to cast to int.
Now other situation. Double can include every byte contained within a float value as it is a bigger datatype. So can we say float f = 10.543; As 10.543 is quite a small value and should fit within a float. Also literal value for such number is considered a Double so compiler should implicitly cast it to float. But it's not so, compiler stops us. We have to append an F or f after that value.
Why are these two conflicting behaviour there for literal value assignment? In short if byte b = 7 is possible. Why is float f = 10.543 not possible?
You can read JLS 5.2 Assignment Conversion
The compile-time narrowing of constants means that code such as:
byte theAnswer = 42;
is allowed. Without the narrowing, the fact that the integer literal 42 has type int would mean that a cast to byte would be required:
byte theAnswer = (byte)42; // cast is permitted but not required
If the type of the expression cannot be converted to the type of the variable by a conversion permitted in an assignment context, then a compile-time error occurs.
If the type of the variable is float or double, then value set conversion (§5.1.13) is applied to the value v
JLS #3.10.2.Floating-Point Literals
A floating-point literal is of type float if it is suffixed with an ASCII letter F or f; otherwise its type is double and it can optionally be suffixed with an ASCII letter D or d
5.1.2. Widening Primitive Conversion
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. A double NaN is converted to a float NaN and a double infinity is converted to the same-signed float infinity.
I hope above clarifies your doubt.
To add to the previous answers, the actual representation of 10.543 is:
float: 10.54300022125244140625
double: 10.5429999999999992610355548095
Since you are actually specifying two different numbers, it makes sense to require an explicit declaration.
A difference :
when you're "converting" from int to byte, you're just truncating the bytes
when you're converting from double to float, you're making a complex not trivial operation, which can't be implicit
Assigning a double to a float can cause precision loss, so java tells you you need to explicitly say how you want it to perform the assignment. Simple truncating may result in significant rounding errors.
Consider that a limited decimal in base 10 may actually be an unlimited fractional value in binary (e.g. floating point) base. Making conversion between floating point types explicit is therefore a good rule of thumb, useful in the vast majority of cases.
For integral types like byte the situation is slightly different: integral types can only differ on their size, but they all have the same decimal precision, which is zero. So there's no ambiguity in assigning a fitting value of a bigger integral type to a smaller integral variable.
"Behind the scene the code is byte b = (byte) 7".
That's not correct. See JLS #5.2 as referred to in several other answers. It says "A narrowing primitive conversion may be used if the type of the variable is byte, short, or char, and the value of the constant expression is representable in the type of the variable."
Nothing there about a typecast.
Related
Is there any difference between how type conversion happens in case of positive and negative numbers?
For example, if we have
short a = 100;
and put it to
int b = a;
if we change '100' to '-100', does it make any difference?
I tried to find it compiling in IDEA, but didn't find difference, but I have this questions from my mentor.
Disclaimer: Since this is a homework question, what I say here might not be the "expected" answer.
There are two conversions involved here. The first one is a narrowing primitive conversion from int (the literal 100 evaluates to a value of type int) to short. The second one is a widening primitive conversion from short to int.
The second conversion will never lose information, as per the JLS §5.1.2:
A widening primitive conversion does not lose information about the
overall magnitude of a numeric value in the following cases, where the
numeric value is preserved exactly:
from an integral type to another integral type
from byte, short, or char to a floating point type
from int to double
from float to double in a strictfp expression (§15.4)
The first conversion is done like this, according to the JLS §5.1.3
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.
Both -100 and 100 is representable with short, whose range is -65536...65535, so no information is lost here either.
In short, it doesn't matter whether you use 100 or -100, the result will be that b will store the value of 100 or -100 respectively.
With the following code:
Float a = 1.2;
there is an error because it takes the decimal as double value and double is a bigger datatype than float.
Now, it takes integer as default int type. So, why is the following code not giving any error?
Byte b = 20;
The compiler is smart enough to figure out that the bit representation of 20 (an int value) can fit into a byte with no loss of data. From the Java Language Specification §5.1.3:
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. A double NaN is converted to a float NaN and a double infinity is converted to the same-signed float infinity.
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.
See also this thread.
There are no implicit narrowing conversions in general - constant expressions are the only exception, and they are explicitly allowed by JLS 5.2:
In addition, if the expression is a constant expression (§15.28) of type byte, short, char, or int:
* A narrowing primitive conversion may be used if the type of the variable is byte, short, or char, and the value of the constant expression is representable in the type of the variable.
There is no mention of implicit narrowing conversions being allowed for floating point numbers, so they are forbidden as per the general rule.
Consider following statement:
byte by = 5; //works fine
literal '5' is of type int and small enough to fit into a variable of type byte. Compiler does the implicit type casting here (from int to byte).
Now consider following scenario:
float fl = 5.5; //compilation error
literal '5.5' is of type double, also small enough to fit into a variable of
type float. Why do we need to explicitly type cast like this:
float fl = (float) 5.5; //works fine
Why compiler is not doing the casting for us in case of floating points?
In the integer version, the compiler knows that all the data in the number 5 can be stored in a byte. No information is lost. That's not always true for floating point values. For example, 0.1f isn't equal to 0.1d.
Now for the example, you've given, the decimal value 5.5 is exactly represented in both float and double, so you could argue that in that case, no information is lost - but it would be pretty odd for the language specification to have to make this valid:
float f = 5.5;
but this invalid:
float f = 5.6;
The language specification is happy to talk about whether a number fits within the range of float/double (although even that isn't as simple as you might expect) but when it comes to whether a literal can be exactly represented, I don't think it ever goes into detail.
The easy answer is, because the specification says so (compile-time constants of type integer can be assigned to smaller types as long as they fit).
With floating-point however, there is not so much determining whether the constant fits, but rather the loss of precision that comes along with it. E.g. assigning 1.23456789123 to a double is fine, but to a float is not. It's not so obvious why, in this case, though, at least to some programmers. I'd definitely count it as a surprise when some floating-point constants work while others won't and the reason isn't as clear as with integral types (where the limits are often second nature to most).
Note that even with doubles there sometimes is lost information. You can make your constants as precise as you want, but you won't always get the exact value you stated in the variable.
Agreed with Jon, However, I would like to add that
byte by = 5; //works fine until the number is less than 128
This is because one byte can only hold upto -128 to 127. Once you will try to enter number above 127, you will get the same error like you get when storing double value into float.
byte by = 128; //compilation error
So for agreeing the lost of the conversion data, you need to perform the explicit conversion.
byte by = (byte) 128; // work fine
Perhaps the most significant reason that Java makes allowance for implicit narrowing conversions of literals of type int to short and byte, but does not do so for conversions of literal double values to float is that Java includes float literals, but does not allow literals of types byte and short.
Personally, I really dislike Java's numerical conversion rules, but the allowance for storing integer constants to short and byte makes those types at least somewhat bearable.
1.when we assign double to float variable compiler gives us error
float f = 2753.2211;
possible loss of precision Required to cast.
2.when we assign int to byte variable compiler don't gives us error
byte b = 24;
OK
In second case data can also be lost.Then why casting explicitly is not necessary.?
The second case is explicitly allowed by the JLS as a special case. In JLS 5.2, which deals with narrowing conversions, it says:
In addition, if the expression is a constant expression (§15.28) of type byte, short, char, or int:
A narrowing primitive conversion may be used if the type of the variable is byte, short, or char, and the value of the constant expression is representable in the type of the variable.
...
In other words, for the non-long integer-like values, you can implicitly narrow them iff the value you're narrowing is a constant that fits within the type you're specifying.
I'm guessing the same trick isn't applied to floats because floating point values are trickier than integer values. For instance, 0.1 can't be exactly expressed in a floating point, while 1.0 can. That means that double d = 0.1 isn't actually putting the value 0.1 into d -- just a value that's very close to 0.1. Given that, the "does it fit exactly" rule doesn't apply even to floating point literals, and the question of "does it fit exactly when we narrow" becomes trickier. Your options would basically be:
always allow it (which could cause some surprising behavior if a value is significantly different than its literal representation)
only allow it if the value can be exactly put in. This would look highly inconsistent:
float f1 = 1.0 and double d1 = 1.0 both work
double d2 = 0.1 works, but float f2 = 0.1 doesn't -- confusing!
never allow it (slightly inconvenient, because you have to type the f char in the literal)
Given these options, it seems that the designers of the JLS chose the least of three not-great options.
Data isn't going to be lost in the second case.
A byte comprises the values of -128 and 127 inclusive, so as long as your int fits within that range, no loss of precision can occur.
Your second value is not a float-literal; by default, all floating point values in Java are double. You can explicitly make that a float by adding f to the end of it.
float f = 2573.2211f;
int is a 32-bit signed integer, byte is an 8-bit signed integer. A byte runs from -128 to 127, while a int runs from -2147483648 to 2147483647
Precision isn't lost, but do not cast a big int into a small byte or you will lose data.
Byte byte1=10;
Short short1=20;
Integer integer=30;
In the above code autoboxing happens successfully
see the below code here I am doing casitng explicitly because It is taking 20 as integer numeric literal by default.
Byte byte1=new Byte((byte) 20);
Short short1=new Short((short) 20);
but see the below code , I have to use l,f and d explicitly without this it is showing error........what is the reason behind it.I am not getting it.
Long long1=40l;
Float float1=50f;
Double double1=60d;
Auto-boxing doesn't include automatic widening of primitives.
The default type of a constant integer numeric expression in java is int, so those numbers are ints, which will be auto boxed to Integers if required. Automatic widening will occur when a narrower primitive type is assigned to a wider type, such as an int to a long.
But the two compiler actions will not both happen; that's why you need to get the primitive constant expression to the appropriate type so that the auto boxing will be to the correct type.
This is part of the Language Specification for floating point literals, ie. F or f for float, and D or d for double
For decimal floating-point literals, at least one digit (in either the
whole number or the fraction part) and either a decimal point, an
exponent, or a float type suffix are required.
For integers,
An integer literal is of type long if it is suffixed with an ASCII
letter L or l (ell); otherwise it is of type int (§4.2.1).
The unboxing conversions are, among other,
From type long to type Long
From type float to type Float
From type double to type Double
So unless your types are the correct type, you cannot unbox.
As for the Byte, Short, Integer, the JLS again comes to the rescue
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.
In your case, an explicit cast isn't necessary because the values fall within the range of their specific type. If instead, you had
Byte b = 1251;
that would create a compilation error, solved by doing
Byte b = (byte)1251;
but losing information in the value.