Home > Back-end >  How are number types converted when using casting?
How are number types converted when using casting?

Time:10-24

I run this code:

public class Try {   
    public static void main(String[] args) {

        int num = (byte)129;        
        System.out.println(num); // result is: -127
    
    }
}

I know, in Java - primitive types are saved on the stack (not on the heap). So I have an on the stack a variable called 'num' - now, according to the code - we need to take the number 129 - cast it to 'byte' data type and save it in 'num.'

My question is: where does Java manage all the quick calculations in terms of memory? On the stack? I guess that we allocate a temporary integer variable (4 bytes) and save the value 129 inside it. and we get:

00000000 00000000 00000000 10000001

When we cast to byte type, Java takes the last 8 bits and copies the value to a temporary byte variable: 10000001. value was equated to 129 but is -127 because the method to save numbers is the Two's complement. 10000001 is -127 so what is printed is -127. Now the value that should be in 'num' is -127.

11111111 11111111 11111111 10000001

Can someone tell me if the process that I described here is correct?

CodePudding user response:

I know, in Java - primitive types are saved on the stack (not on the heap)

This is incorrect.

In java, local variables and parameters are on the stack, and fields (as in, entire objects, whose actual memory load is defined by their fields) are stored on heap.

But, also, all variables except primitives are references (pointers). When you write:

void example() {
  List<String> y = new ArrayList<String>();
}

Then a new arraylist object is made (objects do not have names; it would be incorrect to say that this is 'object y'), and this is done in the heap. Then, the local variable named 'y' (which is on stack, and is a pointer type: Its value is just a small-ish number that you can never see but which the JVM that runs your code uses to 'look up' where an object might 'live') - is updated so that it references (points at) this created object.

Thus, the 'house' lives in the heap, and you have a little notebook that contains an address, and that notebook lives on the stack here.

where does Java manage all the quick calculations in terms of memory? On the stack?

That's not how it works. Stack and heap are where objects live. The CPU is a separate concept. Calculations run on CPU, and as part of calculating things, stack and/or heap may be required to be looked at.

Imagine the heap as a giant blackboard, truly enormous, where you can write down lots of stuff. Entire lists, gigantic memory structures - but also simple numbers (given: class Example { int y; } and elsewhere: Example x = new Example(5), that 5 is on heap: There's an entire Example instance in the heap: Somebody drew a box somewhere on that blackboard, put 'Example' right at the top left of it, and then a '5' inside: See, that 5 is in the heap.

The stack is a piece of paper you hold in your hand. It cannot contain full objects (boxes). It just contains numbers, chars, booleans, and x/y coordinates of where to look on the blackboard. x is on the stack (on that piece of paper), and what's on that piece of paper is the coordinates on the blackboard.

The CPU is you. You are neither the piece of paper nor the blackboard. When I ask you to do or calculate something you might look at your piece of paper. You may even have to walk over to the blackboard and find a box whose coordinates you found on that piece of paper, or not. Depends on what I asked you to do.

all the quick calculations

Turning an int into a byte isn't a calculation. It's copying. It's exactly the same as int x = y; - that copies whatever is at y (even Object x = y; - in that case you're copying that address, not the entire object). byte x = (byte) y; also just copies y to x. But, given that bytes are, well, 1 byte large and an int is 4 bytes large, it only copies one of the 4 bytes. Specifically, the 'least significant' one (whether that's the first or last depends on your CPU architecture. It's the first if its running on intel and the last if it is running on just about anything else, but that's a detail that the JVM fully and perfectly hides from you, it never matters, you never need to know, no code can be observed to run differently when running on intel vs. non-intel).

In other words, calculations? Not happening here.

I guess that we allocate a temporary integer variable (4 bytes) and save the value 129 inside it.

No need. That 129 is already loaded in memory (it's part of your class file, obviously).

cast it to 'byte' data type and save it in 'num.'

So, no. This is just a single operation. There is:

00000000 00000000 00000000 10000001

someplace in memory. Then there is 10000001 in memory.

Let's compile this code, then decompile it, and look at the bytecode.

> cat Test.java
class Test {
  static void foo() {
    int x = 129;
    byte b = (byte) x;
  }
}
> javac Test.java
> javap -v Test
[snip irrelevant stuff]
         0: sipush        129
         3: istore_0
         4: iload_0
         5: i2b
         6: istore_1
         7: return
[snip irrelevant stuff]

When the JVM goes through this line by line:

  • SIPUSH 129 pushes constant value 129 on the stack. In java, the stack is ALL 32-bit/64-bit sized elements. Why? Cuz the spec says so.

  • ISTORE_0 this pops the top-of-stack in 'slot 0' (which is also in stack memory).

  • ILOAD_0 this pushes whatever is in slot 0 on top-of-stack; the value is also still in slot 0. Why is javac generating this, as it is useless? It just does. javac isn't the smart kid in the class. The JVM itself (java.exe) is. If you run this method a lot java will make an optimized copy of this method, catering to exactly your hardware, OS, and observed behaviour so far. It will definitely not be bothering to store and load again for no reason. 'slot 0' here is basically for variable x. Even though this code doesn't use x after this operation.

  • I2B - this opcode does nothing at all. Except tell the JVM and class verifier that the datatype of what's on top-of-stack is now a byte. It's still the full 4 bytes in memory (00000000 00000000 00000000 00000000 10000001), as java's stack is treated as chunks of 32-bit sized units. Why? Because most CPUs simply cannot do anything else. They can't operate on anything smaller for general purpose computing (they'd read in the full 32 or even 64 bit and then you can ignore parts of that if you really must, but the infrastructure required to make that work takes MORE time than just rounding everything up).

  • ISTORE_1 - store this value in slot 1 too (popping it off stack in process). It's because we've assigned to a variable ('b') even though it is unused and could thus be optimized right out.

  • the code then returns with an empty stack as our method has no return value.

  •  Tags:  
  • java
  • Related