preface

The previous article covered local variable push, constant push, and off-stack local variable load instructions, so let’s get started with arithmetic instructions

An overview of arithmetic instruction


role

= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

An arithmetic instruction is used to perform a particular operation on a value on two operand stacks and to push the result back onto the operand stack

classification

= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

In general, arithmetic instructions can be divided into two types: those that operate on integer data and those that operate on floating point data.

Byte, short, char, and booleanl types

= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

Within each of these categories, there are specialized arithmetic instructions for specific data types of the Java virtual machine.

There is no direct support for byte, short, char, and Boolean arithmetic instructions. All operations on these data are handled using int instructions. In addition, when processing arrays of types Boolean, byte, short, and char, they are also converted to use the corresponding byte code instruction of type int.

Overflow during operation

= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

Data operations can lead to overflows, such as the addition of two large positive integers, which may result in a negative number.

In fact, the Java virtual machine specification does not specify the specific results of overflows of integer data, only the division instruction and the remainder instruction when the partition is 0 will cause the virtual machine to throw the ArithmeticException.

Operation mode

= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

Rounding mode to the nearest number:

The JVM requires that when performing floating-point calculations, all results must be rounded to the appropriate precision. Inexact results must be rounded to the nearest exact value that can be represented. If there are two representable forms as close as that value, the one with the least significant zero bit is preferred

Rounding to zero mode:

This mode is used when converting a floating point number to an integer. This mode selects the most accurate rounding result from the target numeric type that is closest to, but not greater than, the original value

NaN values using

= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

When an operation generates an overflow, it will be represented by a signed infinity, and if the result of an operation is not mathematically defined, it will be represented by a NaN value. And all arithmetic operations that use a NaN value as an operand will return a NaN as a result;

Next we can use the sample code to experience NaN and divisor throw sample code

public class ArithmeticTest { @Test public void method1(){ int i = 10; double j = i / 0; system.out.print1n(j); } // The output is as follows:  java.lang.Arithmeticception:/ by zero at com.atguigu.java.ArithmeticTest.method1(ArithmeticTest.java:15)<22 internal calls>

At this point we change the divisor from 0 to 0.0 for double and run the result

public class ArithmeticTest { @Test public void method1(){ int i = 10; Double j = I / 0.0; system.out.print1n(j); Double d1 = 0.0; I double d2 = d1/0.0; system.out.println(d2); //NaN: not a number}} // The output is as follows

If we say that the divisor is the same as the dividend, we should return 1, but since the numerator is also 0, we cannot confirm the exact value

All operational instructions for arithmetic instructions

Instruction is introduced

= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

  • Addition instructions: iADD, LADD, Fadd, dADD
  • Subtraction instruction: ISUB, lSUB, FSUB, dSUB
  • Multiplication instructions: IMul, LMUL, FMUL, DMUL
  • Division instructions: idiv, ldiv, fdiv, ddiv
  • Remainder instructions: irem, lrem, frem, drem //remainder: remainder
  • Negation: ineg, lNEG, fneg, and dNEg
  • Increment instruction: IINC
  • Bit operations (shift instructions) : ISHL, ISHR, IUSHR, LSH1, LSHR, lusHR
  • Bit operations (by bit or instruction) : IOR, LOR
  • Bit operations (bits and instructions) : IAND, LAND
  • Bitwise operations (bitwise Xor instructions) : IXor, LXOR
  • Comparison instruction: DCMPG, DCMPL, FCMPG, FCMPL, LCMP

Let’s use sample code to demonstrate the above directive

public class ArithmeticTest { public void method2(){ f1oat i = 10; float j = -i; i = -j; }}

Next we compile the code and use the plug-in to see what the specific bytecode instructions look like.

We mentioned earlier that float has a range of values from 0 to 2, and if it goes beyond that, the LDC instruction presses the operand stack

Also, after the top element in the operand stack is popped, load the local variable table at the specified position, using fload_1 for float type

At this time, we carry out the operation and take out the local variable table index as: 1 for operation

When we reverse the operand, we press the finished result into the new position of the local variable table

At this point, we find that the code is still inverting, so it is the same as before and pushes the local variable table again

Let’s use a second example code to demonstrate the above directive

public class ArithmeticTest { public void method3(int j){ int i = 100; i = i + 10; }}

We mentioned earlier that int range values have different ranges, which have specific instructions to operate on

Now let’s press into the local variable table instructions, see what is going on

As above, we need to pop the operation and again put the result in the index position of the original local variable table

What if we modify the sample code to add += and see what the specific bytecode would look like?

public class ArithmeticTest { public void method3(int j){ int i = 100; i += 10; }}

Next we compile the code and use the plug-in to see what the specific bytecode instructions look like.

Let’s analyze it by the bytecode and see, what does += do?

Let’s use the fourth example code to demonstrate the above directive

public class ArithmeticTest {
    public int method4(){
        int a = 80;
        int b = 7;int c = 10;
        return (a + b)*c;
    }
}

Next we compile the code and use the plug-in to see what the specific bytecode instructions look like.

We’re not going to go through all the instructions like that, but we’re going to focus on the operand instructions

Let’s use a fifth example code to demonstrate the above directive

public class ArithmeticTest { public int method5(int i ,int j){ return ( (i + j - 1) &~(j - 1)); }}

Next we compile the code and use the plug-in to see what the specific bytecode instructions look like.

We’re not going to go through all the instructions like that, but we’re going to focus on the operand instructions



Let’s use sample code to demonstrate ++ I from a bytecode perspective

Public class ArithmeticTest {// about (ex)++ and (ex)++ public void method6(){int I = 10; ++i; }}

Next we compile the code and use the plug-in to see what the specific bytecode instructions look like.

Let’s use sample code to demonstrate i++ from a bytecode perspective

Public class ArithmeticTest {// about (ex)++ and (ex)++ public void method6(){int I = 10; i++; }}

Next we compile the code and use the plug-in to see what the specific bytecode instructions look like.

We find that they are the same bytecode when no other operators are involved, so let’s take a look

public class ArithmeticTest { public void method7(){ int i = 10; int a = i++; int j = 20; int b = ++j; }}

Next we compile the code and use the plug-in to see what the specific bytecode instructions look like.



Three, the arithmetic instruction comparison instruction


Compare instructions

= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

The comparison instruction compares the size of two elements at the top of the stack and pushes the result

Comparison instructions are: DCMPG, DCMPL, FCMPG, FCMPL, LCMP

Like the previous instruction, the first character d denotes double, f denotes float, and l denotes long

For double and float numbers, there are two versions of the comparison instruction each because of NaN

Take float as an example, there are two FCMPG and FCMPL instructions. The difference lies in the different results of processing NaN values when comparing numbers

The instructions DCMPL and DCMPG are similar, and their meanings can be inferred from their names, which will not be repeated here

The LCMP instruction is for long integers. Since long integers have no NaN value, there is no need to prepare two sets of instructions

For example, compare the

= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

The instructions FCMPG and FCMPL both pop two operands off the stack and compare them

Let the top element of the stack be v2, and the second element in the core top order be v1

  • If v1 is equal to v2, I push in 0
  • If v1 is greater than v2, push 1 in
  • If v1 is less than v2, I push 1 in

The difference between the two instructions is that if a NaN value is encountered, FCMPG pushes in 1, while FCMPL pushes in -1