preface

We learned about bitwise operators before, so a left shift << is equal to multiplying by 2, and a right shift >> is equal to dividing by 2. But I looked at the JDK source found a >>> three symbols, do not understand what this is, went to search, found a lot of knowledge points, sorted it out.

First of all, we know that the programs we write are ultimately in the bottom of the computer, and the bottom of the computer only supports 0 and 1 symbols. So there was a keyboard on the Internet that only had 0 and 1 keys, and that was the big guy’s keyboard. Digress…

Let’s review how many bytes and bits Java primitives account for:

type The number of bytes digits Size range
byte 1 8 – 2 8 ^ ^ ^ ^ 8-1 ~ 2
short 2 16 – 2 ^ 16 ^ ~ ^ 2 ^ 16-1
int 4 32 – 2 ^ 32 2 ^ ^ ~ ^ 1
long 8 64 – 2 64 ^ ^ ^ ^ 64-1 ~ 2
float 4
double 8
char 2 16 A char type can store a Chinese character
boolean 1 true or false

A shift operation is an operation that treats data as a binary number and moves it several bits to the left or right. In Java, there are three types of shift operators: the << left-shift operator, the >> signed right-shift operator, and the >>> unsigned right-shift operator. All three operators can operate only on the four basic integer types long,int,short, and byte, and on the char type. Other types such as double cannot use bitwise operators, so you can experiment with this in the IDE.

In Java, the first digit is used to indicate the positive or negative of a number, zero for a positive number, and 1 for a negative number. For example, we take the simplest 8-bit byte type: 0000, 0000 means 0,0111, 1111, which means the maximum value (2^8^-1), which becomes 1000, 0000, which becomes the minimum value (-2^8^). If I add one more, it’s going to be 1000, 0001 and it’s going to be -127. So you go from 0 to maximum and then to minimum, and then from minimum to zero again.

Left-shift operator<<

The left-shift operator << moves the data to the left several bits after converting it to binary, discarding the high bits and filling the low bits.

First we can use a Java method to get the binary of a number: integer.toBinaryString (int val).

Then let’s look at this example:

public static void main(String[] args) {
  int a = 10;
		System.out.println("Left before binary :"+Integer.toBinaryString(a));
		a <<= 2;
		System.out.println("Left shifted binary :"+Integer.toBinaryString(a));
		System.out.println("Left-shifted decimal :"+a);
}
Copy the code

First define a number with a value of 10, print its binary (1010), and shift it two bits to the left. Prints the shifted result and binary.

Binary before left shift:1010Left shifted binary:101000Left-shifted decimal:40
Copy the code

As you can see, you’ve moved the original binary two places to the left, followed by a zeroing. 40 is 10 times 2 times 2. So a shift to the left is equal to doubling this number. For N to the left, that’s 2 to the N, so 40 is 10 times 2 squared. Let’s look at the left shift of a negative number:

int b = -8;
System.out.println("Left before binary:" + Integer.toBinaryString(b));
b <<= 2;
System.out.println("Left shifted binary:" + Integer.toBinaryString(b));
System.out.println("Left-shifted decimal:" + b);
Copy the code

We define a negative number (-8), print out its binary, move it two bits to the left, print out its binary after moving it to the left, and print it out in base 10.

Binary before left shift:11111111111111111111111111111000Left shifted binary:11111111111111111111111111100000Left-shifted decimal: -32
Copy the code

You can clearly see that the binary has been moved two bits to the left, discarding the preceding position and zeroing the following position. Switching to base 10 also works with our previous calculation: -32 = -8 *2 *2.

Signed right shift operator>>

In the left shift, it moved to the left, discarding the high, zeroing the low. However, there is a sign bit in the right shift operation, and improper operation will cause the answer to be different from the expected result.

A signed right shift is to move several bits to the right of **, discarding the low bits and filling the high bits according to the sign bits. ** For positive numbers to move to the right, add 0; When negative numbers move to the right, the high value is added by 1.

Here’s another example:

public static void main(String[] args) {
   int a = 1024;
   System.out.println("Binary a before right shift:" + Integer.toBinaryString(a));
   a >>= 4;
   System.out.println("Binary of a shifted to the right:" + Integer.toBinaryString(a));
   System.out.println("Decimal of a shifted right :"+a);
   int b = -70336;
   System.out.println("B binary before right shift:" + Integer.toBinaryString(b));
   b >>= 4;
   System.out.println("B right shifted binary:" + Integer.toBinaryString(b));
   System.out.println("B right-shifted decimal :"+b);
}
Copy the code

Two variables are defined, a=1024, and then moved 4 bits to the right. B =-70336 and we moved 4 places to the right. Print them out in binary and decimal respectively.

A Binary before right shift:10000000000A Right shifted binary:1000000A Right-shifted decimal:64B Binary before right shift:11111111111111101110110101000000B Right shifted binary:11111111111111111110111011010100B Right-shifted decimal notation :-4396
Copy the code

As the original binary of A moves to the right, the lower digit is discarded, and the higher digit is replaced by the sign bit, which is 0. When the original binary of B is moved to the right, the low bit is discarded and the high bit is replaced by the sign bit 1. This is also a sign of our previous operation: 1024/2 ^4^ =16; Minus 70336/2 ^4^ = minus 4396.

Unsigned right shift operator>>>

With the signed right shift operator, we move to the right with a high sign, with positive numbers filled with 0 and negative numbers filled with 1. The unsigned right-shift operator is now roughly the same as the right-shift operator, except that positive and negative numbers are no longer differentiated, resulting in high zeros and low ones discarded.

Here’s another example:

public static void main(String[] args) {
   int a = 1024;
   System.out.println("Binary a before right shift:" + Integer.toBinaryString(a));
   a >>>= 4;
   System.out.println("Binary of a shifted to the right:" + Integer.toBinaryString(a));
   System.out.println("Decimal of a shifted right :"+a);
   int b = -70336;
   System.out.println("B binary before right shift:" + Integer.toBinaryString(b));
   b >>>= 4;
   System.out.println("B right shifted binary:" + Integer.toBinaryString(b));
   System.out.println("B right-shifted decimal :"+b);
}
Copy the code

Again, the signed right shift example: this time we’ll just replace the operator with an unsigned right shift operator.

By definition, there is no change in positive numbers, because positive numbers are also high zeros in a signed shift to the right. It just changes when it’s negative, so let’s see if the output matches the guess.

A Binary before right shift:10000000000A Right shifted binary:1000000A Right-shifted decimal:64B Binary before right shift:11111111111111101110110101000000B Right shifted binary:1111111111111110111011010100B Right-shifted decimal:268431060
Copy the code

It’s true that the positive numbers didn’t change, which proves our guess. And then we have negative numbers, and when we move to the right, we fill up the high ones, and discard the low ones. The changed values no longer fit our previous pattern.

In the unsigned shift to the right, when it’s positive, it’s still the same thing as dividing by 2. But it’s no longer true when it’s negative.

What happens when the number of digits shifted exceeds the number of digits occupied by the value?

This is an interesting question, we just moved 2 or 4 bits, what happens if we exceed the number of ints, which is 32 bits? If we move a positive number 32 bits to the left and fill the low zero 32 times to become zero, as the code says, what is the final result of a. Does it go to zero?

public static void main(String[] args) {
  int a = 10;
  a <<= 32;
  System.out.println(a);
}
Copy the code

After we run it, we find that the result of A is the same as 10. If we move it 33 places to the left, it’s going to be 20. Student: Does it work by just moving the remainder of the pair of digits when you go beyond the digit number? For example, if you operate on int, it’s actually %32.

After many tests, it turns out that the answer is indeed this conjecture. When working with ints, the right shift of x bits is x%32 bits.

Is it the same for other types?

We just used int, so is it the same for byte,short,char,long?

First look at the byte type.

public static void main(String[] args) {
   byte b = -1;
	 System.out.println("Before operation:"+b);
	 b >>>= 6;
	 System.out.println("After operation:"+b);
}
Copy the code

The byte value is defined as -1, 1111, 1111, and then unsigned 6 bits to the right, with high zeros added and low ones discarded, so it should become 0000 0011, which is 3. So let’s run this code and see if the printed message is 3?

Before operation: -1After operation: -1
Copy the code

The results are inconsistent with what we expected!

Let’s print out its binary as well to see what happens:

public static void main(String[] args) {
   byte b = -1;
   System.out.println("Decimal before operation:"+b);
   System.out.println("Pre-operation binary:"+Integer.toBinaryString(b));
   b >>>= 6;
   System.out.println("Binary after operation:"+Integer.toBinaryString(b));
   System.out.println("Operation after decimal:"+b);
}
Copy the code

Now look at the results

Decimal before operation: -1Pre-operation binary:11111111111111111111111111111111Post-operation binary:11111111111111111111111111111111Decimal after operation: -1
Copy the code

Java converts byte,short, and char to int before performing a shift. Since we have reassigned it to the original byte type, we have also performed a down-conversion from int to byte, which is truncation. Let’s modify the above example to make it more intuitive:

public static void main(String[] args) {
   byte b = -1;
   System.out.println("Decimal before operation:"+b);
   System.out.println("Pre-operation binary:"+Integer.toBinaryString(b));
   System.out.println("Decimal after 6 unsigned right shift:"+(b>>>6));
   System.out.println("Binary after operation:"+Integer.toBinaryString(b>>>6));
}
Copy the code

Instead of reassigning with =, I print the decimal and binary results directly after the calculation.

Decimal before operation: -1Pre-operation binary:11111111111111111111111111111111Do an unsigned shift to the right6Decimal after bits:67108863Post-operation binary:11111111111111111111111111
Copy the code

It is obvious from the printed result that it is converted to int first and then bit operation is performed. After bit operation, truncation is performed due to reassignment.

For long, it is 64-bit and does not need to be converted first.

conclusion

Shifters are basic operators in Java, and the only types actually supported are int and long. Any byte,short, or char shift is converted to int before it is performed. Left-shifting the << operator is equivalent to multiplying by 2. The signed right-shift operator >> is equivalent to dividing by 2. Using bitwise operators in Java is more efficient than multiplying * and dividing /. The unsigned right shift character >>> will add zeros to the high position and discard the low position. It will still be the same as dividing by 2 in positive numbers, but will be larger in negative numbers (from negative to positive).

This article is published by OpenWrite! Blogger email: [email protected], if you have any questions, you can communicate via email.