In C, are the shift operators (<<
, >>
) arithmetic or logical?

- 347,512
- 102
- 1,199
- 985

- 1,789
- 2
- 11
- 5
-
1What is the meaning of arithmetic and logical? Related question for signed ints: http://stackoverflow.com/questions/4009885/arithmetic-bit-shift-on-a-signed-integer – Ciro Santilli OurBigBook.com Aug 09 '16 at 16:03
11 Answers
When shifting left, there is no difference between arithmetic and logical shift. When shifting right, the type of shift depends on the type of the value being shifted.
(As background for those readers unfamiliar with the difference, a "logical" right shift by 1 bit shifts all the bits to the right and fills in the leftmost bit with a 0. An "arithmetic" shift leaves the original value in the leftmost bit. The difference becomes important when dealing with negative numbers.)
When shifting an unsigned value, the >> operator in C is a logical shift. When shifting a signed value, the >> operator is an arithmetic shift.
For example, assuming a 32 bit machine:
signed int x1 = 5;
assert((x1 >> 1) == 2);
signed int x2 = -5;
assert((x2 >> 1) == -3);
unsigned int x3 = (unsigned int)-5;
assert((x3 >> 1) == 0x7FFFFFFD);

- 951,095
- 183
- 1,149
- 1,285
-
70So close, Greg. Your explanation is nearly perfect, but shifting an expression of signed type and negative value is implementation-defined. See ISO/IEC 9899:1999 Section 6.5.7. – Robᵩ Sep 22 '08 at 22:53
-
18@Rob: Actually, for left shift and signed negative number, the behaviour is undefined. – JeremyP Apr 04 '12 at 15:24
-
6Actually, left shift also results in undefined behavior for *positive* signed values if the resulting mathematical value (which isn't limited in bit size) can't be represented as a positive value in that signed type. The bottom line is that you have to tread carefully when right shifting a signed value. – Michael Burr Jun 21 '13 at 00:30
-
@MichaelBurr: Have *any* production-level compilers written between 1985 and 2010 for hardware designed after 1985 *not* implemented `V<
– supercat Apr 15 '15 at 06:50 -
4@supercat: I really don't know. However, I do know that there are documented cases where code that has undefined behavior causes a compiler to do very non-intuitive things (usually due to aggressive optimization - for example see the old Linux TUN/TAP driver null pointer bug: http://lwn.net/Articles/342330/). Unless I need sign-fill on right shift (which I realize is implementation defined behavior), I usually try to perform my bit shifts using unsigned values, even if it means using casts to get there. – Michael Burr Apr 15 '15 at 07:11
-
2@MichaelBurr: I know that hypermodern compilers use the fact that behavior that wasn't defined by the C standard (even though it had been defined in 99% of *implementations*) as a justification to turn programs whose behavior would have been fully defined on all platforms where they could be expected to run, into worthless bunches of machine instructions with no useful behavior. I'll admit, though (sarcasm on) I'm puzzled by why compiler authors have missed the most massive optimization possibility: omit any part of a program which, if reached, would result in functions being nested... – supercat Apr 15 '15 at 14:30
-
2...more than three deep. There are real-world platforms where even four-deep nesting of methods with a significant number of local variables could bomb (e.g. running an MS-DOS program under very low-memory conditions), and the standard doesn't say that a program which could legitimately bomb on a system with under 2K of free memory shouldn't bomb on a system with two gigs. – supercat Apr 15 '15 at 14:35
-
@supercat I'm interested in the similar question for >> : `int x; x>>n` is 'implementation defined' for `x<0`; are there any implementations for machines (outside of museums) where `x>>n` does *not* reliably give you `x` divided by `2^n` (but rounded towards -inf, and always considering the denom to be > 0)? I guess these would be machines using sign-magnitude or 1's complement instead of 2's complement (I know of no C compiler for the Apollo Guidance Computer). For << you can always cast to unsigned and back to stay out of trouble. – greggo Sep 15 '21 at 18:20
-
-
@greggo: Prior to the invention of `unsigned`, implementations even on two's-complement platforms were split on whether `x>>y` should do an arithmetic or logical shift, but a lot of code written for implementations that happened to use a logical shift relied upon such behavior. The authors of the Standard wanted to avoid mandating that implementations whose customers might have such code change in a way that would break it. As for `<<`, there's no reason why two's-complement implementations shouldn't process it in a fashion equivalent to repeated multiplication in cases where... – supercat Sep 15 '21 at 19:25
-
...the behavior of the latter would be defined, but *the authors of the Standard generally assume there should be no need to waste ink mandating things that would be obvious to anyone who isn't being deliberately obtuse*. The only situations where anyone should care whether the Standard defines the behavior of `x<
– supercat Sep 15 '21 at 19:28 -
@supercat - thank you. It's helpful to know that 'unsigned' was not present from day 1. But neither was 'undefined behaviour' in its current form; anytime 'undefined behaviour' appears, I don't know how 'obvious' anything really is. My understanding is that if the optimizer can prove that a << of negative number always occurs under condition 'c', it is then free to assume that condition 'c' does not occur, which could have all kinds of strange effects. So, it doesn't seem safe to use even if you know what the actual arithmetic does. Maybe in some case it's safe, but only without -flto; yikes. – greggo Sep 18 '21 at 18:59
-
1@greggo: Undefined Behavior in its current form is a result of some "clever" people sometime around 2005 figuring that because "non-portable or erroneous" as meaning "non-portable and therefore erroneous" [hint: it doesn't] any code which invokes Undefined Behavior is "broken", ignoring the published Rationale for the C Standard which specifies that it, among other things, identifies areas of "conforming language extension". – supercat Sep 18 '21 at 21:58
According to K&R 2nd edition the results are implementation-dependent for right shifts of signed values.
Wikipedia says that C/C++ 'usually' implements an arithmetic shift on signed values.
Basically you need to either test your compiler or not rely on it. My VS2008 help for the current MS C++ compiler says that their compiler does an arithmetic shift.
-
1Regarding [this answer](https://stackoverflow.com/a/6488645/1862638) it is not only the compiler but the combination of compiler and (processor) architecture the behavior depends on. – stephan Oct 23 '20 at 16:34
-
3@stephan: A compiler's choice may be in some cases be motivated by processor architecture, but most of today's compilers will process `>>` with signed values as an arithmetic right shift *even when it is necessary to add sign-extension code*. – supercat Sep 16 '21 at 15:02
TL;DR
Consider i
and n
to be the left and right operands respectively of a shift operator; the type of i
, after integer promotion, be T
. Assuming n
to be in [0, sizeof(i) * CHAR_BIT)
— undefined otherwise — we've these cases:
| Direction | Type | Value (i) | Result |
| ---------- | -------- | --------- | ------------------------ |
| Right (>>) | unsigned | ≥ 0 | −∞ ← (i ÷ 2ⁿ) |
| Right | signed | ≥ 0 | −∞ ← (i ÷ 2ⁿ) |
| Right | signed | < 0 | Implementation-defined† |
| Left (<<) | unsigned | ≥ 0 | (i * 2ⁿ) % (T_MAX + 1) |
| Left | signed | ≥ 0 | (i * 2ⁿ) ‡ |
| Left | signed | < 0 | Undefined |
† most compilers implement this as arithmetic shift
‡ undefined if value overflows the result type T; promoted type of i
Shifting
First is the difference between logical and arithmetic shifts from a mathematical viewpoint, without worrying about data type size. Logical shifts always fills discarded bits with zeros while arithmetic shift fills it with zeros only for left shift, but for right shift it copies the MSB thereby preserving the sign of the operand (assuming a two's complement encoding for negative values).
In other words, logical shift looks at the shifted operand as just a stream of bits and move them, without bothering about the sign of the resulting value. Arithmetic shift looks at it as a (signed) number and preserves the sign as shifts are made.
A left arithmetic shift of a number X by n is equivalent to multiplying X by 2n and is thus equivalent to logical left shift; a logical shift would also give the same result since MSB anyway falls off the end and there's nothing to preserve.
A right arithmetic shift of a number X by n is equivalent to integer division of X by 2n ONLY if X is non-negative! Integer division is nothing but mathematical division and round towards 0 (trunc).
For negative numbers, represented by two's complement encoding, shifting right by n bits has the effect of mathematically dividing it by 2n and rounding towards −∞ (floor); thus right shifting is different for non-negative and negative values.
for X ≥ 0, X >> n = X / 2n = trunc(X ÷ 2n)
for X < 0, X >> n = floor(X ÷ 2n)
where ÷
is mathematical division, /
is integer division. Let's look at an example:
37)10 = 100101)2
37 ÷ 2 = 18.5
37 / 2 = 18 (rounding 18.5 towards 0) = 10010)2 [result of arithmetic right shift]
-37)10 = 11011011)2 (considering a two's complement, 8-bit representation)
-37 ÷ 2 = -18.5
-37 / 2 = -18 (rounding 18.5 towards 0) = 11101110)2 [NOT the result of arithmetic right shift]
-37 >> 1 = -19 (rounding 18.5 towards −∞) = 11101101)2 [result of arithmetic right shift]
As Guy Steele pointed out, this discrepancy has led to bugs in more than one compiler. Here non-negative (math) can be mapped to unsigned and signed non-negative values (C); both are treated the same and right-shifting them is done by integer division.
So logical and arithmetic are equivalent in left-shifting and for non-negative values in right shifting; it's in right shifting of negative values that they differ.
Operand and Result Types
Standard C99 §6.5.7:
Each of the operands shall have integer types.
The integer promotions are performed on each of the operands. The type of the result is that of the promoted left operand. If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behaviour is undefined.
short E1 = 1, E2 = 3;
int R = E1 << E2;
In the above snippet, both operands become int
(due to integer promotion); if E2
was negative or E2 ≥ sizeof(int) * CHAR_BIT
then the operation is undefined. This is because shifting more than the available bits is surely going to overflow. Had R
been declared as short
, the int
result of the shift operation would be implicitly converted to short
; a narrowing conversion, which may lead to implementation-defined behaviour if the value is not representable in the destination type.
Left Shift
The result of E1 << E2 is E1 left-shifted E2 bit positions; vacated bits are filled with zeros. If E1 has an unsigned type, the value of the result is E1×2E2, reduced modulo one more than the maximum value representable in the result type. If E1 has a signed type and non-negative value, and E1×2E2 is representable in the result type, then that is the resulting value; otherwise, the behaviour is undefined.
As left shifts are the same for both, the vacated bits are simply filled with zeros. It then states that for both unsigned and signed types it's an arithmetic shift. I'm interpreting it as arithmetic shift since logical shifts don't bother about the value represented by the bits, it just looks at it as a stream of bits; but the standard talks not in terms of bits, but by defining it in terms of the value obtained by the product of E1 with 2E2.
The caveat here is that for signed types the value should be non-negative and the resulting value should be representable in the result type. Otherwise the operation is undefined. The result type would be the type of the E1 after applying integral promotion and not the destination (the variable which is going to hold the result) type. The resulting value is implicitly converted to the destination type; if it is not representable in that type, then the conversion is implementation-defined (C99 §6.3.1.3/3).
If E1 is a signed type with a negative value then the behaviour of left shifting is undefined. This is an easy route to undefined behaviour which may easily get overlooked.
Right Shift
The result 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 non-negative value, the value of the result is the integral part of the quotient of E1/2E2. If E1 has a signed type and a negative value, the resulting value is implementation-defined.
Right shift for unsigned and signed non-negative values are pretty straight forward; the vacant bits are filled with zeros. For signed negative values the result of right shifting is implementation-defined. That said, most implementations like GCC and Visual C++ implement right-shifting as arithmetic shifting by preserving the sign bit.
Conclusion
Unlike Java, which has a special operator >>>
for logical shifting apart from the usual >>
and <<
, C and C++ have only arithmetic shifting with some areas left undefined and implementation-defined. The reason I deem them as arithmetic is due to the standard wording the operation mathematically rather than treating the shifted operand as a stream of bits; this is perhaps the reason why it leaves those areas un/implementation-defined instead of just defining all cases as logical shifts.
-
1Nice answer. With regard to rounding (in the section titled **Shifting**) - right shift rounds towards `-Inf` for both negative and positive numbers. Rounding towards 0 of a positive number is a private case of rounding towards `-Inf`. When truncating, you always drop positively weighted values, hence you subtract from the otherwise precise result. – ysap Mar 11 '18 at 01:30
-
1@ysap Yeah, good observation. Bascially, round towards 0 for positive numbers is a special case of the more general round towards −∞; this can be seen in the table, where both positive and negative numbers I'd noted it as round towards −∞. – legends2k Mar 11 '18 at 16:35
Here are functions to guarantee logical right shift and arithmetic right shift of an int in C:
int logicalRightShift(int x, int n) {
return (unsigned)x >> n;
}
int arithmeticRightShift(int x, int n) {
if (x < 0 && n > 0)
return x >> n | ~(~0U >> n);
else
return x >> n;
}

- 30,738
- 21
- 105
- 131

- 2,360
- 4
- 25
- 28
In terms of the type of shift you get, the important thing is the type of the value that you're shifting. A classic source of bugs is when you shift a literal to, say, mask off bits. For example, if you wanted to drop the left-most bit of an unsigned integer, then you might try this as your mask:
~0 >> 1
Unfortunately, this will get you into trouble because the mask will have all of its bits set because the value being shifted (~0) is signed, thus an arithmetic shift is performed. Instead, you'd want to force a logical shift by explicitly declaring the value as unsigned, i.e. by doing something like this:
~0U >> 1;

- 761
- 4
- 9
When you do - left shift by 1 you multiply by 2 - right shift by 1 you divide by 2
x = 5
x >> 1
x = 2 ( x=5/2)
x = 5
x << 1
x = 10 (x=5*2)

- 71
- 1
- 1
Well, I looked it up on wikipedia, and they have this to say:
C, however, has only one right shift operator, >>. Many C compilers choose which right shift to perform depending on what type of integer is being shifted; often signed integers are shifted using the arithmetic shift, and unsigned integers are shifted using the logical shift.
So it sounds like it depends on your compiler. Also in that article, note that left shift is the same for arithmetic and logical. I would recommend doing a simple test with some signed and unsigned numbers on the border case (high bit set of course) and see what the result is on your compiler. I would also recommend avoiding depending on it being one or the other since it seems C has no standard, at least if it is reasonable and possible to avoid such dependence.

- 44,224
- 30
- 113
- 140
-
Although most C compilers used to have an arithmetic left-shift for signed values, such helpful behavior seems to have been deprecated. Present compiler philosophy seems to be to assume that the performance of a left-shift on a variable entitles a compiler to assume that the variable must be non-negative and thus omit any code elsewhere that would be necessary for correct behavior if the variable was negative. – supercat Apr 16 '15 at 05:47
gcc will typically use logical shifts on unsigned variables and for left-shifts on signed variables. The arithmetic right shift is the truly important one because it will sign extend the variable.
gcc will will use this when applicable, as other compilers are likely to do.

- 16,045
- 8
- 30
- 61

- 9,814
- 12
- 50
- 50
Left shift <<
This is somehow easy and whenever you use the shift operator, it is always a bit-wise operation, so we can't use it with a double and float operation. Whenever we left shift one zero, it is always added to the least significant bit (LSB
).
But in right shift >>
we have to follow one additional rule and that rule is called "sign bit copy". Meaning of "sign bit copy" is if the most significant bit (MSB
) is set then after a right shift again the MSB
will be set if it was reset then it is again reset, means if the previous value was zero then after shifting again, the bit is zero if the previous bit was one then after the shift it is again one. This rule is not applicable for a left shift.
The most important example on right shift if you shift any negative number to right shift, then after some shifting the value finally reach to zero and then after this if shift this -1 any number of times the value will remain same. Please check.

- 16,045
- 8
- 30
- 61

- 1,315
- 24
- 28
According to many c compilers:
<<
is an arithmetic left shift or bitwise left shift.>>
is an arithmetic right shiftor bitwise right shift.
-
4"Arithmetic right shift" and "bitwise right shift" are different. That's the point of the question. The question asked, "Is `>>` arithmetic or bitwise (logical)?" You answered "`>>` is arithmetic or bitwise." That does not answer the question. – wchargin Jan 08 '14 at 02:46
-
1