3
long a = (long)Math.pow(2, 32);
// a = 4294967296 :)

long a = (int)(long)Math.pow(2, 32);
//a = 0 ?!

long a = (int)Math.pow(2, 32);
//a = 2147483647 WTF??!!!

The first expression is obvious. a is printed as it is.

The second expression is a bit confusing. The large value is

100000000000000000000000000000000 // 1 followed by 32 ZEROs, 33 bits in all

When it is forced into an int, how is it taken as ZERO? Shouldn't it take the most significant 1 as sign bit and think that the number is -2147483648 ? [DOUBT CLEARED]

Also, when the double returned from Math.pow (4.294967296E9) is directly cast into int, why is it 2147483647?

I am reading up type casting and data types from a book but the text doesn't explain much. I am confused. Please explain why the second and third expression produce those results.

3 Answers 3

3

No, since it truncates everything past the first 32 bits as far as I know -- so it has no idea about the 1 at the beginning (or rather, at the end).

4294967296 = 0x100000000 (in hexadecimal), and taking only the first 32 bits gives you zero.

Edit: I actually think the other one has to do with a special cast from floating-point to int, that's not the same as going through a long and then an int, since it's a completely different kind of cast to the processor.

Sign up to request clarification or add additional context in comments.

4 Comments

You are saying it truncates everything past the first 32 bits. But the 1 is inside the "first 32 bits" when written as a binary number. What you are saying will be true if there were 32 ZEROs and then there was a 1. But there are 31 ZEROs and then a 1. If it truncates past 32 bits, 1 shouldn't be truncated!
2^32 is a 1 followed by 32 zeroes in binary. Same way 10^10 is a one followed by 10 zeros in decimal. coppervinesoftware.com/2to32.png
Oops! My bad. Sorry :) Now I get it.. :D +1
0x100000000 has eight zeros, so that's 8*4=32 zero bits. The 1 is the 33rd bit, which is thrown away.
2

Section 5.1.3 of the Java spec covers this.

http://java.sun.com/docs/books/jls/second_edition/html/conversions.doc.html#25363

Integer narrowing simply takes the lowest n bits, meaning that sign is not taken into consideration. And, in fact, negative numbers may become positive. Widening does not have this property, and will sign-extend the number into the wider type.

int value = (int)(long)(Math.pow(2, 31)); // double to long to int
System.out.println(value); // Prints -2147483648

long lvalue = value; // int back to long
System.out.println(value); // Prints -2147483648 again

Floating-point narrowing is a much more complicated process, that basically truncates the number to the closest representation possible in the target type, rounding towards zero. In this case, the overflow/underflow rule is triggered, converting the float to the maximum/minimum value representable by the type, respectively.

1 Comment

+1 for the detailed answer of part 3 of the question, although shouldn't we be linking to the JLS 3e? java.sun.com/docs/books/jls/third_edition/html/…
0

From the spec, section 5.1.3. T is one of byte, short, char, or int.

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.

You're ending up with the low-order bits, which makes this case go to 0.

3 Comments

If only the darn book had those 4 lines. One whole page of text describing everything else BUT what you quoted from the spec. Thanks for clearing it :) +1.. I doubted that the sign bit will be ignored but couldn't confirm it.
Is the "spec" link you gave the latest for java?
The first link leads to the latest edition, the second one to the previous. But type casting isn't changed, new Java versions are fully compatible, only some new things like generics were added.