I'm making a small Java program which encrypts any type of file. The way I'm doing it, is the following: I open the input file, read it in a byte array with the same size as that file, then do the encoding, and write the whole array to a .dat file called output.dat. To index the byte array, I'm using a variable of type int. The code:
for(int i : arr) {
if(i>0) {
arr[i] = arr[i-1]^arr[i];
}
}
'arr' is a byte array with the same size as the input file.
The error I get: CodingEvent.java:42: error: possible loss of precision
arr[i] = arr[i-1]^arr[i];
(an arrow spots on the ^ operator)
required: byte
found: int
What's wrong? Could you help me please?
The result of byte ^ byte is, counter-intuitively, int. Use a cast on the result of the expression when assigning it back to arr[i]:
arr[i] = (byte)(arr[i-1]^arr[i]);
This is because the operator is defined as doing a binary numeric promotion on its operands, and so what it's really doing (in this case) is:
arr[i] = (int)arr[i-1]^(int)arr[i];
...which naturally results in int. Which is why we need the cast back.
The operands of the ^ operators are first converted to an int (it is called binary numeric promotion). So both bytes (arr[i-1] and arr[i]) are converted to an int and the result of the operation is an int too.
You need to cast the result back to a byte to assign it to arr[i].
If arr[] is of type byte[] then that's the problem, when java does any binary operation with integers, it returns wither an int or long depending of the operators. In this case the result of arr[i-1]^arr[i] is an int that you're trying to store in a byte.
Look at the JLS 15.22.1
When both operands of an operator &, ^, or | are of a type that is convertible (§5.1.8) to a primitive integral type, binary numeric promotion is first performed on the operands (§5.6.2).
And JLS 5.6.2
1.If any operand is of a reference type, it is subjected to unboxing conversion (§5.1.8).
2.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.
Hence the below expression is first converted to an int .
arr[i-1]^arr[i];
To cast it back to byte , use an explicit cast :
arr[i] = (byte)(arr[i-1]^arr[i]);
Related
In Java when I print something like this
System.out.println(1.0f*1l));
I get an output of
1.0
or with
System.out.println(((byte)1)*'A');
I get an output of
65
In both cases, one of the types is bigger than the other. Longs are 64-bits while floats are 32-bits, and bytes are 8-bits while chars are 16-bits. Despite this, Java outputs the result in the smaller type. Wouldn't this be considered a loss of precision?
Java Language Specification provides a list of rules governing the type of result based on the types of operands.
In particular, section 4.2.4 says that
If at least one of the operands to a binary operator is of floating-point type, then the operation is a floating-point operation, even if the other is integral.
which explains why float "wins" against a long.
Integer arithmetic is explained in section 5.6.2. Specifically, it says that
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.
That is why in your second example the result, 65, is of type int.
Java widens type based on the range of possible values. This means float is considered wider thanlong.
Also integer operations which are not long are performed as int.
A byte * char is an int
You are asking about Binary Numeric Promotion (JLS 5.6.2) (emphasis mine):
When an operator applies binary numeric promotion to a pair of operands, each of which must denote a value that is convertible to a numeric type, the following rules apply, in order:
If any operand is of a reference type, it is subjected to unboxing conversion (§5.1.8).
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.
The expression 1.0f*1l is a float and long, so the long is converted to a float.
The expression ((byte)1)*'A' is a byte and a char, so they are both converted to int.
In addition to the other answers that spell out the rules in detail, I think it's also worth considering why it should be this way. Suppose you do
2.7F * 2L
The true mathematical answer to this multiplication is 5.4. Therefore if the answer were a long, it would have to be 5L (5 is the closest integer to 5.4). Because of this kind of thing it makes much more sense for the result of multiplying a floating-point value by an integer to be a floating point value. I think there would be many more questions about this on Stack Overflow if the following line of code printed 5.
System.out.println(2.7F * 2L);
In fact it prints 5.4.
It also makes sense that all operations between integer types (except long) are done with int. After all, a byte is usually thought of as a small packet of data, and a char is usually thought of as a character. From these perspectives multiplying byte or char values isn't really meaningful. It makes much more sense to keep the rules simple and just do the operations with int.
This question already has an answer here:
Why byte and short values are promoted to int when an expression is evaluated [duplicate]
(1 answer)
Closed 7 years ago.
When we do addition,subtractions,divide(etc..)in byte short data types the result is coming from int value...why is that...?
e.g:- in above code does not compile because the result in c is coming from int why is that happen...??? when we try to print c using system out it came error because result is in int
public class A {
public static void main(String[] args) {
byte a = 12;
byte b = 10;
byte c = a + b;
System.out.println(c);
}
}
According to java doc the when we perform operations on two bytes there is probability that the output would be a number which can't be handled by byte, so it was decided by creators of java to auto cast it to Integer. So it's like this.
This is well answered here, starting from an identical scenario :) http://www.coderanch.com/t/499127/java/java/Adding-bytes
To quote http://java.sun.com/docs/books/jls/third_edition/html/conversions.html#5.6.2
When an operator applies binary numeric promotion to a pair of operands, each of which must denote a value that is convertible to a numeric type, the following rules apply, in order, using widening conversion (§5.1.2) to convert operands as necessary:
If any of the operands is of a reference type, unboxing conversion (§5.1.8) is performed. Then:
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.
The spec says:
https://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.18.2
The binary + operator performs addition when applied to two operands of numeric type, producing the sum of the operands.
The binary - operator performs subtraction, producing the difference of two numeric operands.
Binary numeric promotion is performed on the operands (§5.6.2).
Note that binary numeric promotion performs value set conversion (§5.1.13) and may perform unboxing conversion (§5.1.8).
The type of an additive expression on numeric operands is the promoted type of its operands.
https://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.6.2
When an operator applies binary numeric promotion to a pair of operands, each of which must denote a value that is convertible to a numeric type, the following rules apply, in order:
If any operand is of a reference type, it is subjected to unboxing conversion (§5.1.8).
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.
If you combine the two you see that any operand in an addition will be promoted to either int or long and any primitive smaller than int will be promoted to int. The result of the operation is the type of both operands - which it int.
The reasoning behind that is most probably that int is the most common datatype which reduces probability of overflow while not requiring as much space as a long. The space argument probably isn't as generally valid as it used to be anymore but that part of the spec was defined like in 1995 and an inherent part of the language (which you can't change without breaking compatibility).
Any Binary arithmetic operations we performed on char & byte datatype(and short) promote it to int.
For more details please refer JLS 5.6.2.
This works:
final byte a = 12;
final byte b = 10;
byte c = a + b;
Or this:
byte a = 12;
byte b = 10;
byte c = (byte)( a + b);
In Java, the following is allowed:
char c = 'A' + 1;
Here, c will hold the value 'B'. Above, first the expression is evaluated. So 'A' gets converted to 65, the whole expression evaluates to 66, and then 66 is converted to 'B' since we are storing the value in a char.
The following, however, gives a compile-time error:
char c = 'A';
c = c + 1;
What is the explanation for how Java views the expressions differently? By the way, the following works fine too:
char c = 'A';
c++;
The first example (which compiles) is special because both operands of the addition are literals.
A few definitions to start with:
Converting an int to char is called a narrowing primitive conversion, because char is a smaller type than int.
'A' + 1 is a constant expression. A constant expression is (basically) an expression whose result is always the same and can be determined at compile-time. In particular, 'A' + 1 is a constant expression because the operands of + are both literals.
A narrowing conversion is allowed during the assignments of byte, short and char, if the right-hand side of the assignment is a constant expression:
In addition, if the expression [on the right-hand side] is a constant expression of type byte, short, char, or int:
A narrowing primitive conversion may be used if the variable is of type byte, short, or char, and the value of the constant expression is representable in the type of the variable.
c + 1 is not a constant expression, because c is a non-final variable, so a compile-time error occurs for the assignment. From looking at the code, we can determine that the result is always the same, but the compiler isn't allowed to do that in this case.
One interesting thing we can do is this:
final char a = 'a';
char b = a + 1;
In that case a + 1 is a constant expression, because a is a final variable which is initialized with a constant expression.
The caveat "if […] the value […] is representable in the type of the variable" means that the following would not compile:
char c = 'A' + 99999;
The value of 'A' + 99999 (which is 100064, or 0x186E0) is too big to fit in to a char, because char is an unsigned 16-bit integer.
As for the postfix ++ operator:
The type of the postfix increment expression is the type of the variable.
...
Before the addition, binary numeric promotion* is performed on the value 1 and the value of the variable. If necessary, the sum is narrowed by a narrowing primitive conversion and/or subjected to boxing conversion to the type of the variable before it is stored.
(* Binary numeric promotion takes byte, short and char operands of operators such as + and converts them to int or some other larger type. Java doesn't do arithmetic on integral types smaller than int.)
In other words, the statement c++; is mostly equivalent to:
c = (char)(c + 1);
(The difference is that the result of the expression c++, if we assigned it to something, is the value of c before the increment.)
The other increments and decrements have very similar specifications.
Compound assignment operators such as += automatically perform narrowing conversion as well, so expressions such as c += 1 (or even c += 3.14) are also allowed.
char to int conversion is called widening conversions. In widening conversions, values do not lose information about the overall magnitude of a numeric value where as int to char conversion is called narrowing conversions. With narrowing conversion you may lose information about the overall magnitude of a numeric value and may also lose precision.
For more information on primitive conversions refer this document.
It is because the compiler can check that it ('A' + 1) is within the bounds of a char whereas it cannot (in general) check that c + <an integer> is within the bounds.
Its because the literals for integer or smaller than int as byte ,short and char is int. Understand the following in this way.
code:
byte a = 10;//compile fine
byte b= 11;//compile fine
byte c = a+b;//compiler error[says that result of **a+b** is **int**]
the same happens for any mathematical operations as of 'Divide', 'multiply', and other arithmetic operation. so cast the result to get the literal in desired data type
byte c = (byte)(a+b);
so when you perform
c= c+1;//compiler error
Its the result of c+1 is int not a char . so compiler give a compile time error for the same.
so you need to provide a primitive cast to change the literal to char data type.
Hope this example provide some understanding..
public class CharAndIntTest {
public static void main(String[] args) {
char i='1';
int ii=65;
i=ii;
}
}
An int has larger capacity than a char, so the conversion is not guaranteed to work. The possible range in the value of a char is 0 to 65535, but an int can be anywhere from -2147483648 to 2147483647.
However, there are other considerations other than raw storage size. char is an unsigned type (Java's only built-in unsigned type, actually), so even short and byte variables still need a conversion to char as they may be negative. short and char are both 16 bit, but since short is signed its range is different from that of char (range of short being from -32,768 to 32,767).
The most important thing to learn here is that Java only lets implicit conversions take place if it can be absolutely determined that the conversion will succeed. This applies to primitives, objects, generics, and pretty much everything.
You can force the conversion to happen using a cast:
char i='1';
int ii=65;
i=(char)ii; //explicit cast tells the compiler to force the int into a char
The assignment conversion (JLS, Section 5.2) allows a primitive widening conversion, but not a primitive narrowing 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.
So you must cast to perform a primitive narrowing conversion, unless
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.
So assigning a literal will work:
char a = 65;
chars are 16-bit unsigned numbers, so they got a maximum of 65,635.
ints are 32-bit signed numbers,s o they got a maximum of 2,147,483,647.
So something is bound to go wrong if you sign an int to a char, for all values >= 65,635.
A char is a 16-bit datatype while an int is a 32-bit datatype. Thus, some possible int values can't be represented with a char without dropping some data (truncation). The compiler creates an error message to warn you about the possible problems caused by accidental truncation.
You can still assign int values to char if you do the appropriate casting:
int i=500;
char c = (char)i;
You can even assign illegal char values like this, although the result will still be truncated:
int i = 65555;
char c = (char)i; //still compiles and doesn't cause an exception, but data is lost
i = c; //i's value is 19
Why its printing X88
public static void main(String [] args)
{
char x = 'X';
int i = 0;
System.out.print(true ? x : 0);
System.out.print(false ? i : x);
}
In the first print statement, the type of the conditional expression is char (i.e. 'X') because this part of section 15.25 of the JLS (which is about the type of a conditional expression) applies:
If one of the operands is of type T where T is byte, short, or char, and the other operand is a constant expression of type int whose value is representable in type T, then the type of the conditional expression is T.
So the first statement prints "X".
In the second print statement, that part doesn't apply because i isn't a constant expression. Instead, this section applies:
Otherwise, binary numeric promotion (§5.6.2) is applied to the operand types, and the type of the conditional expression is the promoted type of the second and third operands. Note that binary numeric promotion performs unboxing conversion (§5.1.8) and value set conversion (§5.1.13).
Binary numeric promotion converts the char 'X' to an int (88) and the second statement prints "88" - hence the overall result of "X88".
It's explained in §15.25 of the JLS.
Otherwise, if the second and third
operands have types that are
convertible (§5.1.8) to numeric types,
then there are several cases:
This applies to both statements, since both char and int are numeric types, and thus 5.1.8 trivially applies. So we look at the rules below (excerpted):
[...]
If one of the operands is of type T where T is byte, short, or
char, and the other operand is a
constant expression of type int whose
value is representable in type T, then
the type of the conditional expression
is T.
This applies to the first, since 0 is a constant expression of type int. T is , char, which is why it prints as X.
[...]
Otherwise, binary numeric promotion (§5.6.2) is applied to the
operand types, and the type of the
conditional expression is the promoted
type of the second and third operands.
Note that binary numeric promotion
performs unboxing conversion (§5.1.8)
and value set conversion (§5.1.13).
This applies to the second, since nothing else does. :) The relevant rule from §5.6.2 is simply:
Otherwise, both operands are converted to type int.
The Unicode code point for 'X' is 88.
For the first statement System.out.print(false ? x : 0); the compiler thinks that you want to print a char so 0 will be considered as a char (during compilation).
When you do System.out.print(false ? i : x); the compiler thinks that you want to print an integer, so it will try to convert (widen) 'X' into an integer which is 88.
An interesting thing is that if you try to do this :
System.out.print(true ? x : i);
As i cannot be considered as a char (it would require narrowing) it will print 88
Another thing which is interessant here :
System.out.print(true ? x : 65536);
This time the value 65536 can't be considered as a char at compile time (the max value for a char is 65535 [#See Character.MAX_VALUE]) so it will be considered as an int, so x will be too.
To summarize, if one of the operand is an int and the other a char, there are two possible conversions :
The int is narrowed into a char (loss of precision)
The char is widen into an int (no loss of precision)
To avoid useless loss, Java will chose the second option.
When you do System.out.print(false ? x : 0);, the 0 is converted at compilation time into a char (because the compiler knows that there won't be loss of precision).
Resources :
JLS - Widening Primitive Conversion
Because the expression false ? i : x is being seen as returning an integer by the compiler. Since you are using a char the 'X' is 88 in ASCII and it is being seen as an integer since they are compatible.
Personally, I think there should be at least a compiler warning here.
In the second ternary If, the parameters must be of the same type, otherwise one of the two is implicitly converted.
In your example, x is converted from char to int, and 'X' has an ASCII code of 88.
Others have already answered why this happens.
If you want X to be printed, cast the expression value back to char as:
System.out.print((char)(false ? i : x)); // prints X
or you can also cast i to char so that the type of expression becomes char as:
System.out.print(false ? (char)i : x); // prints X