Double to Int without lossy precision - java

The following declaration gives me a compile-time error "Error:(19, 13) java: incompatible types: possible lossy conversion from double to int"
int i1 = 10.0;
// Error:(19, 14) java: incompatible types: possible lossy conversion from double to int
Question 1
I understand the error but can the compiler not infer - in this particular case - that there will be no loss of precision?
Question 2
If I explicitly convert the double to an int then it compiles fine (not even a warning) even though I am definitely losing precision in this example
int i2 = (int)9999999999999.999999999;
If I can't compile the first case - where no loss occurs - then why does this second example with the explicit conversion not even generate a compiler warning?

In theory, a compiler could infer that the specific example in #1 wouldn't actually loose precision based on the literal's value, but as you've seen - it doesn't.
The explicit cast signals to the compiler that you are aware of the situation an handling it, so the code actually compiles. Most IDEs, however, could be configured to emit a warning in such a situation too.

The assignment of a double value to an int variable would require a narrowing conversion, even if the value is a compile-time constant, and hence requires an explicit cast.
Except that an int (or short or char) constant value can be assigned to a byte, short, or char variable without casting, if the constant actually fits in the variables value range, as explicitly documented in the Java Language Specification, section 5.2. Assignment Contexts:
Assignment contexts allow the value of an expression to be assigned (§15.26) to a variable; the type of the expression must be converted to the type of the variable.
Assignment contexts allow the use of one of the following:
an identity conversion (§5.1.1)
a widening primitive conversion (§5.1.2)
a widening reference conversion (§5.1.5)
a boxing conversion (§5.1.7) optionally followed by a widening reference conversion
an unboxing conversion (§5.1.8) optionally followed by a widening primitive conversion.
If, after the conversions listed above have been applied, the resulting type is a raw type (§4.8), an unchecked conversion (§5.1.9) may then be applied.
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.
A narrowing primitive conversion followed by a boxing conversion may be used if the type of the variable is:
Byte and the value of the constant expression is representable in the type byte.
Short and the value of the constant expression is representable in the type short.
Character and the value of the constant expression is representable in the type char.

Answer to question 1
There is a loss of precision according to the compiler, because the compiler only sees that you have an int variable on the left and a double value on the right. The compiler is not so smart as to figure out that 10.0 will not lose precision when converted to an int.
The compiler could in theory be built to allow that statement to compile, but there is no real benefit in doing so. Almost no one writes int x = 10.0.
Answer to question 2
Indeed, there is a loss of precision, but why did the compiler not complain? Because you used a cast. You wrote (int). This is you showing the compiler that you know what you're doing. By writing a cast, you are telling it that you are aware that there is a possible loss of precision.

Interesting question
In theory this could be possible, well javac does not complain when doing:
short s = 12;
Even if 12 here is a compile time constant of type int, so it can deduce that no precision is lost. I guess this is what the compiler team thought would be most appropriate.
Well when you cast is a different story, it's like saying "trust me, I know what I'm doing", even if you loose precision.

Related

Java Numeric Types [duplicate]

Consider the below code snippet:
// automatic casting works for int to byte conversion as integer literal 127
// is in the range for byte
byte b1 = 127; //OK
// automatic casting doesn't work for long to int conversion
// even if long literal is in the range of int.
int i5 = 100L; // NOT OK - compilation error
Is there any explanation for such behavior?
Why is explicit conversion not needed in the case of int to byte, but needed for long to int?
The How does Java convert int into byte? question is different. It is about an issue in implicit conversion of int to byte when the int value is out of range.
Widening conversions (eg. byte to int) are generally accepted implicitly by the Java compiler, as there's no loss of information (the range of int is greater than that of byte).
Narrowing conversions (eg. long to int, as in your case) can cause a loss of information, so are generally required to be explicitly casted.
See this similar question, and this. A relevant piece of the Java Language Specification:
Assignment conversion occurs when the value of an expression is assigned (§15.26) to a variable: the type of the expression must be converted to the type of the variable.
...
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.
A narrowing primitive conversion followed by a boxing conversion may be used if the type of the variable is:
Byte and the value of the constant expression is representable in the type byte
Short and the value of the constant expression is representable in the type short.
Character and the value of the constant expression is representable in the type char.
(emphasis mine)
Some confusion stems from the fact that we're dealing in particular with constant expressions, as we're using numeric literals. The above spec also needs some careful reading.
To clear some things up and directly answer some of the OP's queries:
Why implicit narrowing is supported for one type of narrowing and not the other?
Ie. why does byte b1 = 127 work implicitly, while int i5 = 100L not?
byte b1 = 127 performs an implicit conversion as (cf. the bold text in the above quote), "the value of the constant expression is representable in the type byte". That is, 127 is representable by a byte, so the conversion is implicit. If you try byte b1 = 128, you'll get an error about incompatible types, as 128 isn't representable by byte. The only reason we're allowed an implicit cast here at all is because we're using a constant expression.
We don't get an implicit conversion in int i5 = 100L (even though 100 is in the range of int) as that's simply not listed in the allowed implicit conversions (the variable's type, int, is not one of byte, short, or char).
We also don't get an implicit conversion in byte a = 0L, this time as the constant expression is of type long, not of type byte, short, char, or int.
How will a normal programmer know which narrowing conversion is allowed implicitly?
The implicit narrowing conversions only occur when you're assigning a constant expression to a variable. In these cases, implicit conversions are good as we don't want to be writing code like byte b = (byte)0 all the time. At the same time, we do want to be warned if we write something like byte b = 128, as that doesn't have intuitive behaviour.
When we're not assigning constant expressions (so eg. int x = 0; byte b = x;), we always want to be warned when we're doing a potentially lossy conversion, as they're dangerous - so explicit conversions in this case also make sense.

Does Java always convert addition result into int?

is it true that java converts primitive Type addition (like byte+short) results into integers?
I suspect you are talking about promotion. Its not as simple as always promoting to an int. Section 5.1.2 talks about primitive widening in detail, but here are a few guidelines:
If the result is assigned to a variable then it will be promoted to the type of the variable (if possible)
A more limited type will be promoted to the more expansive type
A result can be promoted if it is likely to be larger than the type will handle
Like I said this is a simplification of the actual rules, but in general Java does the right thing.
The following sections of the Java Language Specification (JLS) are applicable:
Section 5.6 Numeric Promotions) "Numeric promotion is applied to the operands of an arithmetic operator"
Section 5.6.2.2 Binary Numeric Promotion) "...If either operand is of type double...long...float...Otherwise both operands are converted to type int"
So in your example of byte + short, the operands would each first be converted to ints.
To further illustrate:
short a = 1;
short b = 2;
short c = a + b;
will produce a compile error "required short, found int".
You would need to cast the result of the addition (an int) to a short, like this:
short c = (short) (a + b);
You need to know about argument promotion and casting concept.
Java can only evaluate the expressions which have values of the same type. for example if you have the expression a = b + c; then a and b and c must have the same type. you now might wonder so how you can add up an integer and a double without anything goes wrong. this is where the concept of type promotion is applied implicitly by Java. in this case your integer variable is implicitly changed to double only for performing your operation (remember the type of your integer variable is not changed only a double copy is made for your expression).
Now the point is that java only uses type promotion implicitly if the data of your variable is not lost. For example if you change integer to double you will not lose any data. but if you want to change let's say a double variable with value 1.2 to integer then you will lose the fraction part. This is where java will not apply type promotion and you need to do type casting manually. Basically what type casting means is to take control over the compiler and telling to compiler that I know I might lose some data in my variable but it is ok and perform the casting for me.
Type promotion and casting occur in expressions and in method calls.
The table below shows the valid promotions which java would do automatically.
Type Valid Promotion
double None
float double
long float or double
int long, float or double
char int, long, float or double
short int, long, float or double (but not char)
byte short, int, long, float or double (but not char)
boolean None(boolean values are not considered to be numbers in java)

Java widening conversions

I'm preparing for Java 7 certification and have the following question.
Byte b = 10 compiles ok. Looks like the compiler is narrowing int 10 to byte 10 and then boxing it. How come Byte b = new Byte(10) won't compile? Why can't the compiler narrow int 10 to byte 10 like it did in the first case?
Also how come Long l = new Long(10) compiles ok but Long l = 10 fails?
I'm not clear about how this works. Can somebody provide an clear explanation?
Section 5.2 of the JLS covers the types of conversions that are allowed in assignment contexts.
Assignment contexts allow the use of one of the following:
an identity conversion (§5.1.1)
a widening primitive conversion (§5.1.2)
a widening reference conversion (§5.1.5)
a boxing conversion (§5.1.7) optionally followed by a widening reference conversion
Additionally,
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.
A narrowing primitive conversion followed by a boxing conversion may be used if the type of the variable is:
Byte and the value of the constant expression is representable in the type byte.
Short and the value of the constant expression is representable in the type short.
Character and the value of the constant expression is representable in the type char.
Byte b = 10 compiles ok because 10 is a constant expression and is representable as a byte.
Byte b = new Byte(10) won't compile because 10 is an int literal, and method invocation conversion won't perform primitive narrowing conversions. To get this call to a Byte constructor to compile, you can explicitly cast 10 to byte:
Byte b = new Byte( (byte) 10);
Long l = new Long(10) compiles because method invocation conversion will perform primitive widening conversions, including from int to long.
Long l = 10 won't compile, because Java will not specifically allow a widening conversion followed by a boxing conversion, as I discussed in a recent answer. To get this to compile, you can use a long literal, so only boxing is necessary.
Long l = 10L;
The basic rules are:
you can't convert-and-autobox in one step (JLS 5.1.7 defines the boxing conversions, and it doesn't include convert-and-autobox type conversions, so they're not allowed)
you can't implicitly narrow a type
These rules explain why Long l = 10 doesn't work, as well as new Byte(10). The first would require the int literal 10 to be widened to a long and then be boxed, which isn't allowed. (More precisely, it would require a conversion from int to Long, which JLS 5.1.7 doesn't define.) The second would require the int literal 10 to be implicitly narrowed to a byte, which isn't allowed.
But there are exceptions to the rule. Byte b = 10 is 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 followed by a boxing conversion may be used if the type of the variable is:
Byte and the value of the constant expression is representable in the type byte.
(some irrelevant parts omitted)
Lastly, new Long(10) works because the int literal 10 can be automatically widened to a long 10L.
Byte constructor takes either byte type or String type. See this
Constructor for Long takes long as an argument. Since long can take in integer, it allows it in the constructor.
10 is an integer literal, you have to downcast it to pass it to the Byte constructor. There unfortunately is no byte literal syntax to remove the cast either.
Also how come Long l = new Long(10) compiles ok but Long l = 10 fails?
Because 10, an integer, can fit into a long with no issues. An integer cannot fit into a byte, so the cast is needed in that case (widening conversion).
This cast is compile time as well, as it's also a widening conversion. Check out section 5.1.5 in the JLS:
Widening reference conversions never require a special action at run time and therefore never throw an exception at run time. They consist simply in regarding a reference as having some other type in a manner that can be proved correct at compile time.
i think i have a solution for your problem...
//constructor for Byte class
Byte(byte value){
}
There are two rules for java type conversion
Both types are compatible
Destination type is greater than source type
Now in Your case you trying to convert int into byte which is against our second rule....
but below is the solution
Byte b = new Byte((byte)10);
Now let's talk about your Second issue...
Long x = 10;//incompatible type
This is the issue of autoboxing...
Now as we all know that autoboxing automatically converted primitive type into it's wrapper class..
But conversion not happens in case of autoboxing means....int is converted into Integer
byte is converted into Byte..
Now when you assign int primitive type to Long, it gives you error of incompatible type.......
Solution
Long x = (long) 10;//works fine....

Can an integer be added with a long?

I'm sorry for such a lame-o question. I would test this myself... But unfortunately I do not know how to code for java, and it would not be worth answering just for this one question.
Is it possible to add a long and an integer together?
My friend is working on a project, and I think he can fix one of his errors by using a long instead of an integer. (He wants numbers to be higher than 2.147 billion).
I tried doing a bit of research on my own, and I was surprised that the answer wasn't as easy to find. This is one source of information that I was able to find.
"If either or both of the integer types is a long, the result is a long."
https://community.oracle.com/message/5270213
Is that correct? Again, sorry that I'm not able to test this out myself.
Yes, you can add a long and an int just fine, and you'll end up with a long.
The int undergoes a widening primitive conversion, as described in the Java Language Specification, specifically JLS8, §5.1.2. JLS8 §5.6.2 is the important part that details what happens here (my emphasis):
Widening primitive conversion (§5.1.2) is applied to convert either or both operands as specified by the following rules:
If either operand is of type double, the other is converted to double.
_ Otherwise, if either operand is of type float, the other is converted to float.
Otherwise, if either operand is of type long, the other is converted to long.
Otherwise, both operands are converted to type int.
This remains the case even for the (currently) latest JLS18 spec, in 5.6 Numeric contexts:
If any expression is of a reference type, it is subjected to unboxing conversion (§5.1.8).
Next, widening primitive conversion (§5.1.2) and narrowing primitive conversion (§5.1.3) are applied to some expressions, according to the following rules:
If any expression is of type double, then the promoted type is double, and other expressions that are not of type double undergo widening primitive conversion to double.
Otherwise, if any expression is of type float, then the promoted type is float, and other expressions that are not of type float undergo widening primitive conversion to float.
Otherwise, if any expression is of type long, then the promoted type is long, and other expressions that are not of type long undergo widening primitive conversion to long.
And so on ...
Yes you can add a long and an integer together. in java there are several primitive data types. int and long variables are two of them. these two data types are used to store integer values. the difference is the size of the variable,
int : 32bit
long : 64bit
you can add int and long together, when jvm add these two variables, the result is generated as a long value. so you have to use a long variable to store the answer. This is due to the auto type conversion of java.
if you want to get int value as an answer, you have to cast the long value in to int,
int x=5; //int value
long y = 10; //long value
long z = x + y; //result is a long value(normally jvm does)
int i=(int) (x+y); // result is cast into a int value.
both z and i get value: 15
You can always simply try such things. For instance using jdoodle
As you can see it is perfectly possible. It is advisable to use a long to store the result, but not necessary. In case of overflow in the latter case, Java will simply use the 32 least significant bits, thus:
6666555555555544444444443333333333222222222211111111110000000000 (index)
3210987654321098765432109876543210987654321098765432109876543210
----------------------------------------------------------------
0101010101010010000011110010101111101010101111101011101011011101 (value)
will be stored as:
33222222222211111111110000000000 (index)
10987654321098765432109876543210
--------------------------------
11101010101111101011101011011101 (value)

Wrapper classes - why integer literals fail for Long but work for anything smaller

Just trying to understand auto-boxing, which I do apart from one thing:
Short s = 250;
Long l = 250;
The assignment to Long l fails. This, I expect, is because you cannot widen then box (i.e. it tries to widen the int value 250 to a long and then box it which it cannot do).
However, the assignment to Short s works. What is going on to make this fine? My assumption was it is still doing boxing and some kind of conversion. But if it's a case of it knowing 250 fits into a short, why does it not know that 250 will fit into a long?
Normally, you cannot apply multiple (implicit) conversions in assignment (JLS §5.2 Assignment Conversion):
Assignment conversion occurs when the value of an expression is assigned (§15.26) to a variable: the type of the expression must be converted to the type of the variable. Assignment contexts allow the use of one of the following:
an identity conversion (§5.1.1)
a widening primitive conversion (§5.1.2)
a widening reference conversion (§5.1.5)
a boxing conversion (§5.1.7) optionally followed by a widening reference conversion
an unboxing conversion (§5.1.8) optionally followed by a widening primitive conversion.
Long l=250; requires two conversions (widening primitive conversion followed by boxing conversion), that's why it doesn't compile.
Long l=250l; compiles because it requires a single boxing conversion.
But narrowing conversion of a constant expression is a special case, that's why Short s=250; compiles:
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.
A narrowing primitive conversion followed by a boxing conversion may be used if the type of the variable is :
Byte and the value of the constant expression is representable in the type byte.
Short and the value of the constant expression is representable in the type short.
Character and the value of the constant expression is representable in the type char.
Ideally, no auto narrowing should be allowed.
But since there are no byte/short literals, we can't write
byte b = 0b;
and it seems silly to
byte b = (byte)0;
so auto narrowing of constant integer is allowed so we can write
byte b = 0;
which is carried over to autoboxing case.
For long/Long, since there are long literals, this is less of a problem. Still, it should be allowed, since auto widening of signed integer is always safe.

Categories