2
votes

I am currently studying assembly, and with it the workings of bitwise operations amongst them shifting. Specifically arithmetic rightshifting is bothering me.

Now, in the book i am reading there are several practice problems, amongst them, I need to perform this operation on a byte consisting of:

0100 0100

Now, seeing as the arithmetic rightshift fills in the value of the most significant bit, it seems to me this should be right shited like so:

00001000

However, the book states that it should be

11101000

That is, 1's are filled in from the left instead of 0's. But wasn't the most significant bit a 0?

well, there's another as well:

0110 0100 >> 3 = 0000 1100

But, apparently that's wrong as well, and it should be:

11101100

Again, I don't get it the most significan't bit value is clearly 0, the one the furthest to the left, yet the solution tells me 1's should be filled in?


so i have a final one here that apparently is correct:

0111 0010 >> 3 = 0000 1110

This is what I would expect. So why aren't these others correct?

Reading assembly is incredibly hard without understanding this, since a lot of multiplication and division is compiled to shift operations.

3
Which book? (errors in books do happen, and this looks like an error unless there's more context to explain this oddity). Is this supposed to be 2's complement or something else (like one's complement)? Not that that should matter; 0100 0100 is positive in 1's complement as well. Yes, it should shift in zeros to produce a result with the same sign as the initial value.Peter Cordes
The book (which i find to be good otherwise) is: Computer Systems a programmers perspective by Randal E. Bryant and David R. O'Hallaron The Practice Problem is 2.16 on page 94, solution is given on page 184rasmus91

3 Answers

3
votes

A left shift is an increase by the base power a right a decrease. So base 2 (binary) a right shift is a decrease by a power of 2, a left an increase. So right is divide by 2 a left a multiply by 2.

The problem is with the right shift and negative numbers, which means assume twos complement, and also understand the processor doesnt know or care, bits is bits, they mean something to the programmer.

A negative twos complement number means the msbit is set as you clearly understand.

So if I want to divide -2 0b11111110 by 2 to get -1 0b11111111 it would be nice to just right shift that but a logical right shift generally means fill in the top with zeros so 0b11111110 logical shifted right becomes 0b01111111 which is not -1 it is 127. Some processors have an arithmetic right shift which instead of shifting in zeros it duplicates the top bit so 0b11111110 ASR becomes 0b11111111. Which is the right/desired answer for using a shift to divide -2 / 2.

Likewise understand that 0b11111110 at the same time also represents the value 254 and 254/2 = 127. So an arithmetic right shift gives 255 which is the wrong answer. So far for signed we want to do an ASR for unsigned an LSR.

You are correct, your book is probably wrong or you are only giving us fragments of the book.

0b01000100 ASR is 0b00100010
0b01000100 LSR is 0b00100010
0b10001000 ASR is 0b11000100
0b10001000 LSR 0s 0b01000100

Multibit arithmetic shifts just think of them as multiple single bit or just fill the top with the msbit of the original number

0b10001000 ASR 3 = 0bBBB10001 where B was a 1 so 0b11110001 for LSR the BBBes are zeros 0b00010001

Note that an arithmetic shift LEFT and a logical shift left are the same operation they simply fill in zeros from the right.

1
votes

No shift will ever fill with 1 when the input's MSB was 0. Note that the reverse isn't true (logical right shift always fills with zero).

If the book doesn't have some extra context, then it's simply an error. That's not a rotate or anything else. Not even 1's complement or sign/magnitude could explain it (because the number is positive in all 3 of those representations).

Arithmetic right shifts in 2's complement shift in copies of the sign bit. (so for example sar eax, 31 broadcasts the sign bit to all bits of the 32-bit register.

The sign of an arithmetic shift result will always be the same as the sign of the input.


Logical right shifts always shift in zeros.


Left shifts are the same for logical and arithmetic: they shift in zeros. (x86 has a sal mnemonic, but it's just an alternate name for the same opcode as shl. Later x86 extensions don't bother mentioning an arithmetic left shift, e.g. there's a SIMD pslld (packed-integer shift left logical) but no pslad.)

In sign/magnitude, arithmetic left shifts would I guess leave the sign bit untouched. I'm not sure if 1's complement arithmetic left shifts would need any special treatment.

But 2's complement arithmetic left shift is identical to logical. (And identical to add same,same to left shift by 1 aka multiply by 2.)

0
votes

Speaking of C, this would be an error on the book, according to the standard:

  • Shifting on unsigned types works as you expect (the number of bits to shift has to be inferior than the width of the number to be shifted and non negative).
  • Shifting on signed types is different. If the number to be shifted is negative, then the left shift is undefined, however the right shift is implementation-defined.

So, the shifting on unsigned numbers is well-defined, but not for signed numbers (better said, there is no a general definition of).

For completeness sake, the standard says:

The result of E1 >> E2is 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 / 2E2. If E1 has a signed type and a negative value, the resulting value is implementation-defined ().

So taking OP's example into account (positive numbers), there is no reason to fill with one's.

Talking about arithmetic shift (let's use x86 as example):

In an arithmetic shift (also referred to as signed shift), like a logical shift, the bits that slide off the end disappear (except for the last, which goes into the carry flag). But in an arithmetic shift, the spaces are filled in such a way to preserve the sign of the number being slid. For this reason, arithmetic shifts are better suited for signed numbers in two's complement format.

The SAR instruction will fill with one's when the number is negative (in order to keep the sign), for instance:

sar 10110011b, 2 #the result is 11101100b

(shr 10110011b, 2 #the result is 00101100b)

But again, OP's example is talking about positive numbers, so there is no reason to fill with one's.

Summarising, it is possible to fill with one's when a right shift is applied, but not in those cases.