I was practicing some castings in Java and I faced a situation for which I couldn't find any answers, anywhere. There are a lot of similar questions with answers, but none gave me an explanation for this particular case.
When I do something like
long l = 165787121844687L;
int i = (int) l;
System.out.println("long: " l);
System.out.println("after casting to int: " i);
The output is
long: 165787121844687
after casting to int: 1384219087
This result is very intriguing for me.
I know that the type long is a 64-bit integer, and the type int is a 32-bit integer. I also know that when we cast a larger type to a smaller one, we can lose information. And I know that there is a Math.toIntExact() method that is quite useful.
But what's the explanation for this "1384219087" output? There was loss of data, but why this number? How "165787121844687" became "1384219087"? Why does the code even compile?
That's it. Thanks!
CodePudding user response:
165787121844687L in hex notation = 0000 96C8 5281 81CF
1384219087 in hex notation = 5281 81CF
So the cast truncated the top 32 bits as expected.
32-bits
deleted
▼▼▼▼ ▼▼▼▼
165_787_121_844_687L = 0000 96C8 5281 81CF ➣ 1_384_219_087
64-bit long ▲▲▲▲ ▲▲▲▲ 32-bit int
32-bits
remaining
CodePudding user response:
If you convert these two numbers to hexadecimal, you get
96C8528181CF
528181CF
See what's happened here?
CodePudding user response:
The Answer by OldProgrammer is correct, and should be accepted. Here is some additional info, and a workaround.
Java spec says so
Why does the code even compile?
When you cast a numeric primitive in Java, you take responsibility for the result including the risk of information loss.
Why? Because the Java spec says so. Always best to read the documentation. Programming by intuition is risky business.
See the Java Language Specification, section 5.1.3. Narrowing Primitive Conversion. To quote (emphasis mine):
A narrowing primitive conversion may lose information …
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.
Math#…Exact…
When you want to be alerted to data loss during conversion from a long
to a short
, use the Math
methods for exactitude. If the operation overflows, an execution is thrown. You can trap for that exception.
try
{
int I = Math.toIntExact( 165_787_121_844_687L ) ; // Convert from a `long` to an `int`.
}
catch ( ArithmeticException e )
{
// … handle conversion operation overflowing an `int` …
}
You will find similar Math#…Exact…
methods for absolute value, addition, decrementing, incrementing, multiplying, negating, and subtraction.