Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sign extension with bitwise shift operation

following this Q&A I tried to exam the answer so I wrote:

#include <stdio.h>

int main ()
{

        int t;int i;
        for (i=120;i<140;i++){
                t = (i - 128) >> 31;
                printf ("t = %X , i-128 = %X ,  ~t & i = %X , ~t = %X \n", t, i-128 , (~t &i), ~t);
        }

        return 0;
}

and the Output is:

t = FFFFFFFF , i-128 = FFFFFFF8 ,  ~t & i = 0 , ~t = 0 
t = FFFFFFFF , i-128 = FFFFFFF9 ,  ~t & i = 0 , ~t = 0 
t = FFFFFFFF , i-128 = FFFFFFFA ,  ~t & i = 0 , ~t = 0 
t = FFFFFFFF , i-128 = FFFFFFFB ,  ~t & i = 0 , ~t = 0 
t = FFFFFFFF , i-128 = FFFFFFFC ,  ~t & i = 0 , ~t = 0 
t = FFFFFFFF , i-128 = FFFFFFFD ,  ~t & i = 0 , ~t = 0 
t = FFFFFFFF , i-128 = FFFFFFFE ,  ~t & i = 0 , ~t = 0 
t = FFFFFFFF , i-128 = FFFFFFFF ,  ~t & i = 0 , ~t = 0 
t = 0 , i-128 = 0 ,  ~t & i = 80 , ~t = FFFFFFFF 
t = 0 , i-128 = 1 ,  ~t & i = 81 , ~t = FFFFFFFF 
t = 0 , i-128 = 2 ,  ~t & i = 82 , ~t = FFFFFFFF 
t = 0 , i-128 = 3 ,  ~t & i = 83 , ~t = FFFFFFFF 
t = 0 , i-128 = 4 ,  ~t & i = 84 , ~t = FFFFFFFF 
t = 0 , i-128 = 5 ,  ~t & i = 85 , ~t = FFFFFFFF 
t = 0 , i-128 = 6 ,  ~t & i = 86 , ~t = FFFFFFFF 
t = 0 , i-128 = 7 ,  ~t & i = 87 , ~t = FFFFFFFF 
t = 0 , i-128 = 8 ,  ~t & i = 88 , ~t = FFFFFFFF 
t = 0 , i-128 = 9 ,  ~t & i = 89 , ~t = FFFFFFFF 
t = 0 , i-128 = A ,  ~t & i = 8A , ~t = FFFFFFFF 
t = 0 , i-128 = B ,  ~t & i = 8B , ~t = FFFFFFFF 

Why does ~t of any negative number is -1 == 0xFFFFFFFF if t declared as integer ?

like image 616
0x90 Avatar asked Mar 31 '13 13:03

0x90


2 Answers

From: Right shifting negative numbers in C

Edit: According to the Section 6.5.7 of the latest draft standard, this behavior on negative numbers is implementation dependent:

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 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.

And, your implementation is probably doing an Arithmetic Shift with two's complement numbers


Operator >> as Signed right shift or arithmetic right shift, shift all the bits to right a specified number of times. Important is >> fills leftmost sign bit (Most Significant Bit MSB) to leftmost bit the after shift. This is called sign extension and serves to preserve the sign of negative numbers when you shift them right.

Below is my diagrammatic representation with an example to show how this works (for one byte):
Example:

i = -5 >> 3;  shift bits right three time 

Five in two's complement form is 1111 1011 Memory Representation:

 MSB
+----+----+----+---+---+---+---+---+
|  1 |  1 | 1  | 1 | 1 | 0 | 1 | 1 |   
+----+----+----+---+---+---+---+---+
   7    6   5    4   3   2   1   0  
  ^  This seventh, the left most bit is SIGN bit  

And below is, how >> works? When you do -5 >> 3

                        this 3 bits are shifted 
                         out and loss
 MSB                   (___________)      
+----+----+----+---+---+---+---+---+
|  1 |  1 | 1  | 1 | 1 | 0 | 1 | 1 |   
+----+----+----+---+---+---+---+---+
  | \                 \  
  |  ------------|     ----------|
  |              |               |
  ▼              ▼               ▼
+----+----+----+---+---+---+---+---+
|  1 |  1 | 1  | 1 | 1 | 1 | 1 | 1 |
+----+----+----+---+---+---+---+---+
(______________)
 The sign is        
 propagated

Notice: the left most three bits are one because on each shift sign bit is preserved and each bit is right too. I have written The sign is propagated because all this three bits are because of sign(but not data).

[ANSWER]
In your output in

first Eight lines

      ~t is 0
==>    t is FFFFFFFF 
==>    t is -1

(Note: 2's complement of -1 is FFFFFFFF, because 1 = 00000001, 1's complement of 1 is FFFFFFFE, and 2's complement = 1's complement + 1 that is: FFFFFFFE + 00000001 = FFFFFFFF)

So t is always evaluated -1 in first eight times in loop. Yes, How ?

In for loop

for (i=120;i<140;i++){
     t = (i - 128) >> 31;

values of i for first eight time is i = 120, 121, 122, 123, 124, 125, 126 ,127 all eight values are less then 128. So return of (i - 128) = -8, -7, -6, -5, -4, -3, -2, -1. hence in first eight times expression t = (i - 128) >> 31 shift-rights a negative number.

t =   (i - 128)  >> 31
t =  -ve number  >> 31

Because in your system int is of 4 bytes = 32 bits, all right most 31 bits are shift-out and loss and due to propagation of sign bit that is 1 for negative number all bits value becomes 1. (as I have shown in above figure for one byte)

So for fist eight times:

    t =  -ve number  >> 31 ==  -1 
    t = -1
  and this gives 
    ~t = 0

Hence output of fist eight times for ~t is 0.

For remaining last lines

      ~t is FFFFFFFF
==>   ~t is -1   
==>    t is 0 

For remaining last lines, in for loop

for (i=120;i<140;i++){
     t = (i - 128) >> 31;

i values are 128, 129, 130, 132, 133, 134, 135, 136, 137, 138, 139, all are greater then or equals to 128. and sign bit is 0.

So (i - 128) for remaining last lines is >=0 and for all this MSB sign bit = 0. And because again you shifts right 31 times all bits excepts then sigh bit shift-out and sign bit 0 propagates and fills all bits with 0 and magnitude becomes 0.

I think it would be good if I write an example for a positive number too. So we take an example 5 >> 3 and five is one byte is 0000 0101

                        this 3 bits are shifted 
                         out and loss
 MSB                   (___________)      
+----+----+----+---+---+---+---+---+
|  0 |  0 | 0  | 0 | 0 | 1 | 0 | 1 |   
+----+----+----+---+---+---+---+---+
  | \                 \  
  |  ------------|     ----------|
  |              |               |
  ▼              ▼               ▼
+----+----+----+---+---+---+---+---+
|  0 |  0 | 0  | 0 | 0 | 0 | 0 | 0 |
+----+----+----+---+---+---+---+---+
(______________)
 The sign is        
 propagated

See again I writes The sign is propagated, So leftmost three zeros are due to sign bit.

So this is what operator >> Signed right shift do, and preserves the sign of left operand.

like image 162
Grijesh Chauhan Avatar answered Oct 21 '22 12:10

Grijesh Chauhan


Why does t = (i - 128) >> 31 gives zero or -1 for each number?

When a non-negative 32-bit integer is shifted right 31 positions, all non-zero bits get shifted out and the most significant bits get filled with 0, so you end up with 0.

Usually, when a negative 32-bit integer is shifted right 31 positions, the most significant bits don't get filled with 0, instead they get set to the sign of the number and so the sign propagates into all bits and in 2's complement representation all bits set to 1 amount to -1. The net effect is as if you repeatedly divided the number by 2, but with a little twist... The result is rounded towards -infinity instead of towards 0. For example -2>>1==-1 but -3>>1==-2 and -5>>1==-3. This is called arithmetic right shift.

When I say "usually", I mean the fact that the C standard allows several different behaviors for right shifts of negative values. On top of that, it allows non-2's complement representations of signed integers. Usually, however, you have the 2's complement representation and the behavior that I've showed/explained above.

like image 45
Alexey Frunze Avatar answered Oct 21 '22 13:10

Alexey Frunze