Java: right shift on negative number
Because in Java there are no unsigned datatypes, there are two types of right shifts: arithmetic shift >>
and logical shift >>>
. http://docs.oracle.com/javase/tutorial/java/nutsandbolts/op3.html
Arithmetic shift >>
will keep the sign bit.
Unsigned shift >>>
will not keep the sign bit (thus filling 0
s).
(images from Wikipedia)
By the way, both arithmetic left shift and logical left shift have the same result, so there is only one left shift <<
.
why the result of a negative number in shift operator is different from the normal division?
The shift operator drops any mathematical fraction by taking the floor, not the truncation. The JLS, Section 15.19, states:
The value of
n >> s
isn
right-shifteds
bit positions with sign-extension. The resulting value isfloor(n / 2s)
. For non-negative values ofn
, this is equivalent to truncating integer division, as computed by the integer division operator/
, by two to the powers
.
The floor of -3.75
is -4
whereas truncation would have yielded -3
.
When the value is right-shifted, bits are lost as they are shifted "off the end" of the value. This is what is responsible for the floor operation.
-15: 11110001
-4: 11111100 // The rightmost 1 bit above is lost, resulting in what looks like the floor function.
Shifted by negative number in java
From the JLS, section 15.19 (Shift Operators):
If the promoted type of the left-hand operand is int, only the five lowest-order bits of the right-hand operand are used as the shift distance. It is as if the right-hand operand were subjected to a bitwise logical AND operator & (§15.22.1) with the mask value 0x1f (0b11111). The shift distance actually used is therefore always in the range 0 to 31, inclusive.
Now -63 & 0x1f == 1
, So this is actually a shift by 1 to the right, hence the answer of 4.
unsigned right shift by negative number in Java
It seems counter-intuitive, but when shifting an int
, only the last 5 bits of the shift amount are used, according to the JLS, Section 15.19.
If the promoted type of the left-hand operand is
int
, then only the five lowest-order bits of the right-hand operand are used as the shift distance. It is as if the right-hand operand were subjected to a bitwise logical AND operator & (§15.22.1) with the mask value 0x1f (0b11111). The shift distance actually used is therefore always in the range 0 to 31, inclusive.
This means that -3
only has the last 5 bits used, or 29
. Here's -3
in bits to show what's going on:
11111111 11111111 11111111 11111101
The last 5 bits are 11101
, or 29
in decimal.
The right-shift distance is 29
, which means that the first 3 bits are kept and shifted all the way to the right. Taking -37
:
11111111 11111111 11111111 11011010
After the unsigned shift of 29 places, 7
is left:
00000000 00000000 00000000 00000111
As you can see, a negative shift amount is confusing at best. Try to avoid it and always attempt to use an actual number between 0 and 31 for the shift amount for shifting int
s.
Shifting by Negative Numbers
<<
(and other shift operators) only takes 5 least significant bits of its right operand for int
, and 6 for long
, because it makes no sense to shift int
by more than 31.
In your case it's 0b10110
= 22
.
Therefore 1 << (-10)
is equivalent to 1 << 22
.
Bit wise shift operator with shift by negative number
But if b is neagtive, shouldn't it be an error at runtime?
Not according to the Java Language Specification, section 15.19:
If the promoted type of the left-hand operand is int, only the five lowest-order bits of the right-hand operand are used as the shift distance. It is as if the right-hand operand were subjected to a bitwise logical AND operator & (§15.22.1) with the mask value 0x1f (0b11111). The shift distance actually used is therefore always in the range 0 to 31, inclusive.
So a shift of -32 actually ends up as a shift of 0, and a shift of -49 actually ends up as a shift of 15 - hence the results you saw.
Is right shifting undefined behavior for negative number in cpp and in java?
Yes. It is implementation-defined.
According to C++03 5.8/3 which defines right-shifting:
The value of E1 >> E2 is E1 right-shifted E2 bit positions. If E1 has
an unsigned type or if E1 has a signed type and a nonnegative value,
the value of the result is the integral part of the quotient of E1
divided by the quantity 2 raised to the power E2. If E1 has a signed
type and a negative value, the resulting value is
implementation-defined.
For more information, see this link.
Why does right shift on positive number sometimes result in a negative number?
Use the zero-fill right shift operator (>>>
) to always get a positive result:
const now = 1562143596806 // UNIX timestamp in millisecondsconsole.log(now >>> 8)
Signed and unsigned right shift seem to have the same behaviour
This is normal behavior. Any integer with a negative value has a binary representation starting with infinite 1s.
So if you start out with say: -3 the binary representation looks like this:
...11 1101
So if we right shift this by 2 we get
...11 1111
Now for the unsgined/signed right shift. This relies on the fact that we don't have infinite digits in our integer. So say we have an 8bit integer assigned as -3 it looks like this:
1111 1101
If we do a signed shift, it will look at the MSB (most significant bit, the most left one) and preserve the value of that when shifting. So a signed shift right by 3 looks like this:
1111 1111
On the contrary, a unsigned right shift will not check the MSB and just shift right and fill with zeroes resulting in this:
0011 1111
This is exactly what you're seeing but the output truncates the preceding zeroes away.
If you don't know why negative integers are stored that way, check this answer.
As for why (b & 0xff) >>> 5
behaves differently
Integers in java are 32bit, that means the binary representation will have 32 digits. Your -59 will look like the following binary representation:
1111 1111 1111 1111 1111 1111 1100 0101 == -59
0000 0111 1111 1111 1111 1111 1111 1110 == -59 >>> 5
If you now and this together with 0xff
you get the following:
1111 1111 1111 1111 1111 1111 1100 0101 == -59
0000 0000 0000 0000 0000 0000 1111 1111 == 0xff
0000 0000 0000 0000 0000 0000 1100 0101 == -59 & 0xff
0000 0000 0000 0000 0000 0000 0000 0110 == (-59 & 0xff) >>> 5
Related Topics
Stax Xmlstreamreader Check for the Next Event Without Moving Ahead
Java Se 6 VS. Jre 1.6 VS. Jdk 1.6 - What Do These Mean
Fit/Scale Jcomponent to Page Being Printed
How to Build a Docker Container for a Java Application
How to Change the Highlight Color of a Focused Jcombobox
How to Tell Spring Boot Which Main Class to Use for the Executable Jar
How to Ensure in Java That the Current Local Time Is Correct
Automatic Reserved Word Escaping for Hibernate Tables and Columns
Org.Postgresql.Util.Psqlexception: Fatal: Sorry, Too Many Clients Already
Why Does Java's Java.Time.Format.Datetimeformatter#Format(Localdatetime) Add a Year
How to Unit Test Abstract Classes: Extend with Stubs
How to Send List of Objects to View and Back to Post Method in Controller
What Are the Differences Between Char Literals '\N' and '\R' in Java
Gradle - Getting the Latest Release Version of a Dependency
How to Redirect to Another Action Class Without Using on Struts.Xml
Receiving "Wrong Name" Noclassdeffounderror When Executing a Java Program from the Command-Line