Casting result of multiplication two positive integers to long is negative value - java

I have code like this :
int a = 629339;
int b = 4096;
long res = a*b;
The result is -1717194752
but if I add one manual cast to long long res = ((long)a)*b; or long res = (long) a*b; the result is correct 2577772544
Who can explain how does it works.

You have to break the assignment statement into its parts to understand what is doing on:
long res = a*b;
Step 1 is to get the values of a and b.
Step 2 is to evaluate a * b. Since a and b are both ints, this is an int multiplication. So we multiply 629339 by 629339 which would be 2577772544.
Unfortunately, 2577772544 is larger than the largest possible Java int value ... so the multiplication operation silently overflows ... and we get -1717194752 instead.
Step 3 we assign the value of the RHS to the LHS. Since the RHS is int and the LHS is float, the JLS says we perform a primitive widening conversion ... which simply turns -1717194752 into a long with the same value. The widened value is then assigned to res.
To get the answer that you are expecting, we have to force multiplication to be performed using long arithmetic. For example:
long res = ((long) a) * b;
In this case, we have a multiplication of a long by an int, and this is handled by widening the int to a long and performing a long multiply. This no longer overflows (because 2577772544 is well below the largest long value), so when we finally assign the value to res, it is the number that you were expecting.

a*b is an integer, not a long.
Because it's only an integer, it has already wrapped around the 32-bit limit.
Casting this integer back to long will not magically recover that data.

long res = a*b;
a*b will be treated as integer unless you add 'l' at end (or) cast.
As per java tutorial
The int data type is a 32-bit signed two's complement integer. It has a minimum value of -2,147,483,648 and a maximum value of 2,147,483,647 (inclusive). For integral values, this data type is generally the default choice unless there is a reason (like the above) to choose something else.

Related

What's the difference between declaring a long variable with 'L' and without? [duplicate]

This question already has answers here:
Using the letter L in long variable declaration
(2 answers)
Closed 5 years ago.
What's the difference between the following?
long var1 = 2147483647L;
long var2 = 2147483648;
(or any primitive variable declaration)
Does it have any performance issue with or without L? Is it mandatory?
In the first case you are assigning a long literal to a long variable (the L or l suffix indicates long type).
In the second case you are assigning an int literal (that's the default type when no suffix is supplied) to a long variable (which causes an automatic type cast from int to long), which means you are restricted to the range from Integer.MIN_VALUE to Integer.MAX_VALUE (-2147483648 to 2147483647).
That's the reason why
long var2 = 2147483648;
doesn't pass compilation (2147483648 is larger than Integer.MAX_VALUE).
On the other hand
long var2 = 2147483648L;
would pass compilation.
For easy understanding each of the type have range in java.
By default every digit you entered in java is either byte or short or integer.
short s = 32767;
byte b = 127;
int i = 2147483647;
So if you assign anything except from their range you'll get compilation error.
int i = 2147483648; //compilation error.
Type range
And when you write long longNumber = 2147483647;
though it falls in long range but internally java treat it as
long l = (int) 2147483647;
you wont get any errors.
But if we assign beyond the range of integer like
longNumber = 2147483648; we will get compilation error as
long o = (int) 2147483648;
here java will try to convert the 2147483648 to int but it is not in int range so widening error is thrown.
To indicate java that the number what we have written is beyond the integer range just append l or L to the end of the number.
so java will wide his range till long and convert it as
long o = (long) 2147483648;
By default every floating point or digit with floating points (.) are size of double. So when you write some digits with (.) java treat as a double and it must be in double range.
As we know the float range is smaller then double.
so when you write
float f = 3.14;
though it falls in double range but internally java treat this assignment as
float f = (double) 3.14;
here you are assigning the double to float narrowing which is not correct.
so either you have to convert the expression like that
float f = (float)3.14;
or
float f = 3.14f; // tell jvm to assign this in float range by appending **f** or **F**
If we don't mention the L with the value then value is considered to be int value.
It type casts the int to long automatically.

Packing bytes into a long with |= is giving unexpected results

I am trying to concatenate my byte[] data into a long variable. But for some reason, the code is not working as I expected.
I have this byte array which maximum size will be 8 bytes which are 64 bits, the same size a Long variable has so I am trying to concatenate this array into the long variable.
public static void main(String[] args) {
// TODO Auto-generated method stub
byte[] data = new byte[]{
(byte)0xD4,(byte)0x11,(byte)0x92,(byte)0x55,(byte)0xBC,(byte)0xF9
};
Long l = 0l;
for (int i =0; i<6; i++){
l |= data[i];
l <<=8;
String lon = String.format("%064d", new BigInteger(Long.toBinaryString((long)l)));
System.out.println(lon);
}
}
The results are:
1111111111111111111111111111111111111111111111111101010000000000
1111111111111111111111111111111111111111110101000001000100000000
1111111111111111111111111111111111111111111111111001001000000000
1111111111111111111111111111111111111111100100100101010100000000
1111111111111111111111111111111111111111111111111011110000000000
1111111111111111111111111111111111111111111111111111100100000000
When the final result should be something like
111111111111111110101000001000110010010010101011011110011111001
which is 0xD4,0x11,0x92,0x55,0xBC,0xF9
byte in Java is signed, and when you do long |= byte, the byte's value is promoted and the sign bit is extended, which essentially sets all those higher bits to 1 if the byte was a negative value.
You can do this instead:
l |= (data[i] & 255)
To force it into an int and kill the sign before it's then promoted to a long. Here is an example of this happening.
Details
Prerequisite: If the term "sign bit" does not make sense to you, then you must read What is “2's Complement”? first. I will not explain it here.
Consider:
byte b = (byte)0xB5;
long n = 0l;
n |= b; // analogous to your l |= data[i]
Note that n |= b is exactly equivalent to n = n | b (JLS 15.26.2) so we'll look at that.
So first n | b must be evaluated. But, n and b are different types.
According to 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).
Both operands are convertible to primitive integral types, so we consult 5.6.2 to see what happens next. The relevant rules here are:
Widening primitive conversion (§5.1.2) is applied to convert either or both operands as specified by the following rules:
...
Otherwise, if either operand is of type long, the other is converted to long.
...
Ok, well, n is long, so according to this b must be now be converted to long using the rules specified in 5.1.2. The relevant rule there is:
A widening conversion of a signed integer value to an integral type T simply sign-extends the two's-complement representation of the integer value to fill the wider format.
Well byte is a signed integer value and its being converted to a long, so according to this the sign bit (highest bit) is simply extended to the left to fill the space. So this is what happens in our example (imagine 64 bits here I'm just saving space):
b = (byte)0xB5 10110101
b widened to long 111 ... 1111111110110101
n 000 ... 0000000000000000
n | b 111 ... 1111111110110101
And so n | b evaluates to 0xFFFFFFFFFFFFFFB5, not 0x00000000000000B5. That is, when that sign bit is extended and the OR operation is applied, you've got all those 1's there essentially overwriting all of the bits from the previous bytes you've OR'd in, and your final results, then, are incorrect.
It's all the result of byte being signed and Java requiring long | byte to be converted to long | long prior to performing the calculation.
If you're unclear on the implicit conversions happening here, here is the explicit version:
n = n | (long)b;
Details of workaround
So now consider the "workaround":
byte b = (byte)0xB5;
long n = 0l;
n |= (b & 255);
So here, we evaluate b & 255 first.
So from JLS 3.10.1 we see that the literal 255 is of type int.
This leaves us with byte & int. The rules are about the same as above although we invoke a slightly different case from 5.6.2:
Otherwise, both operands are converted to type int.
So as per those rules byte must be converted to an int first. So in this case we have:
(byte)0xB5 10110101
promote to int 11111111111111111111111110110101 (sign extended)
255 00000000000000000000000011111111
& 00000000000000000000000010110101
And the result is an int, which is signed, but as you can see, now its a positive number and its sign bit is 0.
Then the next step is to evaluate n | the byte we just converted. So again as per the above rules the new int is widened to a long, sign bit extended, but this time:
b & 255 00000000000000000000000010110101
convert to long 000 ... 0000000000000000000000000010110101
n 000 ... 0000000000000000000000000000000000
n | (b & 255) 000 ... 0000000000000000000000000010110101
And now we get the intended value.
The workaround works by converting b to an int as an intermediate step and setting the high 24 bits to 0, thus letting us convert that to a long without the original sign bit getting in the way.
If you're unclear on the implicit conversions happening here, here is the explicit version:
n = n | (long)((int)b & 255);
Other stuff
And also like maraca mentions in comments, swap the first two lines in your loop, otherwise you end up shifting the whole thing 8 bits too far to the left at the end (that's why your low 8 bits are zero).
Also I notice that your expected final result is padded with leading 1s. If that's what you want at the end you can start with -1L instead of 0L (in addition to the other fixes).

Why Type Casting of larger variable to smaller variable results in modulo of larger variable by the range of smaller variable

Recently,while I was going through typecasting concept in java, I have seen that type casting of larger variable to smaller variable results in the modulo of larger variable by the range of smaller variable.Can anyone please explain this in detail why this is the case and is it true for any explicit type conversion?.
class conversion {
public static void main(String args[])
{
double a = 295.04;
int b = 300;
byte c = (byte) a;
byte d = (byte) b;
System.out.println(c + " " + d);
}
}
The above code gives the answer of d as 44 since 300 modulo 256 is 44.Please explain why this is the case and also what happens to the value of c?
It is a design decision that goes all the way back the the C programming language and possibly to C's antecedents too.
What happens when you convert from a larger integer type to a smaller integer type is that the top bits are lopped off.
Why? Originally (and currently) because that is what hardware integer instructions support.
The "other" logical way to do this (i.e. NOT the way that Java defines integer narrowing) would be to convert to that largest (or smallest) value representable in the smaller type; e.g.
// equivalent to real thin in real java
// b = (byte) (Math.max(Math.min(i, 127), -128))
would give +127 as the value of b. Incidentally, this is what happens when you convert a floating-point value to an integer value, and the value is too large. That is what is happening in your c example.
You also said:
The above code gives the answer of d as 44 since 300 modulo 256 is 44.
In fact, the correct calculation would be:
int d = ((b + 128) % 256) - 128;
That is because the range of the Java byte type is -128 to +127.
For completeness, the above behavior only happens in Java when the larger type is an integer type. If the larger type is a floating point type and the smaller one is an integer type, then a source value that is too large or too small (or an infinity) gets converted to the largest or smallest possible integer value for the target type; e.g.
double x = 1.0e200;
int i = (int) x; // assigns 'Integer.MAX_VALUE' to 'i'
And a NaN is converted to zero.
Reference:
Java 17 Language Specification: §5.1.3

Arithmetic Operation in Java

Assume that I have one arithmetic function which will add two long variables and return a long value. If pass Long.MaxValue() as an argument it wont give a perfect result. What will be the solution for that? The code below explains what I mean:
public class ArithmaticExample {
public static void main(String[] args) {
System.out.println(ArithmaticExample.addLong(Long.MAX_VALUE, Long.MAX_VALUE));
}
public static long addLong(long a,long b){
return a+b;
}
}
So you can see the wood from the trees, let's recast the problem using byte rather than long and consider
byte a = 0b01111111; // i.e. 127, the largest value of a `byte`.
byte b = 0b01111111;
byte c = (byte)(a + b);
where you need the explicit cast to circumvent conversion of a + b to an int.
Computing c by hand gives you 0b11111110. This is, of course, the bitwise representation of -2 in an 8 bit 2's complement type.
So the answer for the byte case is -2. And the same holds true for a long: there are just more 1 bits to contend with in your addition.
Note that although all this is perfectly well-defined in Java, the same cannot be said for C and C++.
If you need to add two long values of such magnitude then consider using BigInteger.
The result is -2. This is not what is expected as it seems to be more an overflow than anything else, but this result is "normal".
Long.MAX_VALUE = 9223372036854775807
Long.MAX_VALUE + 1 = 9223372036854775807
Long.MAX_VALUE + Long.MAX_VALUE = -2

int(primitive) value not getting typecasted to long(primitive) implicitly

My question appears very simple.
int i =99999;
long square = i*i;
System.out.println(square); //prints 1409865409 - incorrect value
int i = 99999;
long square = (long) i*i;
System.out.println(square); // prints 9999800001 - correct value
It looks to be the range issue of int.
But shouldn't it typecast the product to long implicitly?
What am I missing here?
I know how to use Math.pow(double,double) function and it works perfectly. But
I want to know the reason for above output.
TIA.
In the first case, the result is first computed as an int, and only then converted to a long.
Therefore the wrong result.
In the second case, i is converted to a long before computing the result because (long) (cast to long) has a higher precedence than *.
Therefore the right result.
You have fallen prey to an operator precedence error. This line:
long square = (long) i*i;
actually does this:
long square = ((long) i)*i;
This is important, because when you multiply 99999, you get a number too large to represent as an int. This line:
long square = i*i;
squares i, causing an overflow error, then casts the result to a long and assigns it to square. The other line casts the first factor to a long, forcing it to cast the second factor to a long as well, before the calculation takes place.

Categories

Resources