2

Artichoke101 asked this:

Lets say that I have an array of 4 32-bit integers which I use to store the 128-bit number

How can I perform left and right shift on this 128-bit number?"

My question is related to the answer Remus Rusanu gave:

void shiftl128 (
    unsigned int& a,
    unsigned int& b,
    unsigned int& c,
    unsigned int& d,
    size_t k)
{
    assert (k <= 128);
    if (k > 32)
    {
        a=b;
        b=c;
        c=d;
        d=0;
        shiftl128(a,b,c,d,k-32);
    }
    else
    {
        a = (a << k) | (b >> (32-k));
        b = (b << k) | (c >> (32-k));
        c = (c << k) | (d >> (32-k));
        d = (d << k);
    }
}

void shiftr128 (
    unsigned int& a,
    unsigned int& b,
    unsigned int& c,
    unsigned int& d,
    size_t k)
{
    assert (k <= 128);
    if (k > 32)
    {
        d=c;
        c=b;
        b=a;
        a=0;
        shiftr128(a,b,c,d,k-32);
    }
    else
    {
        d = (c << (32-k)) | (d >> k); \
        c = (b << (32-k)) | (c >> k); \
        b = (a << (32-k)) | (b >> k); \
        a = (a >> k);
    }
}

Lets just focus on one shift, the left shift say. Specifically,

a = (a << k) | (b >> (32-k));
b = (b << k) | (c >> (32-k));
c = (c << k) | (d >> (32-k));
d = (d << k);

How is this left shifting the 128-bit number? I understand what bit shifting is, << shifts bits left, (8-bit number) like 00011000 left shifted 2 is 01100000. Same goes for the right shift, but to the right. Then the single "pipe" | is OR meaning any 1 in either 32-bit number will be in the result.

How is a = (a << k) | (b >> (32-k)) shifting the first part (32) of the 128-bit number correctly?

Community
  • 1
  • 1
KRB
  • 4,205
  • 16
  • 38
  • 53
  • Instead of hardcoding the `32` I would advise using `sizeof(unsigned int)`. It should be `32`, but at least you'll make sure of it. – Matthieu M. Nov 30 '11 at 17:26
  • 2
    `sizeof(unsigned int)` would be 4. :) So, you need `sizeof(unsigned int)*CHAR_BIT`. – Jim Buck Nov 30 '11 at 17:27

6 Answers6

6

This technique is somewhat idiomatic. Let's simplify to just a and b. We start with:

+----------+----------+
|    a     |    b     |
+----------+----------+

and we want to shift left some amount to obtain:

+----------+----------+
|  a    :  |  b    :  |  c  ...
+----------+----------+
|<--x-->|  |
      ->|y |<-

So X is simply a << k. y is the k msbs of b, right-aligned in the word. You obtain that result with b >> (32-k).

So overall, you get:

a = x | y
  = (a << k) | (b >> (32-k))

[Note: This approach is only valid for 1 <= k <= 31, so your code is actually incorrect.]

Oliver Charlesworth
  • 252,669
  • 29
  • 530
  • 650
2

When the bits of a get shifted to the left, something has to fill in the space left over on the right end. Since a and b are conceptually adjacent to each other, the void left by shifting the bits of a gets filled by the bits that are shifted off the end of b. The expression b >> (32-k) computes the bits that get shifted off of b.

Rob Kennedy
  • 156,531
  • 20
  • 258
  • 446
1

You need to remember that it is acceptable, in shifting, to "lose" data.

The simplest way to understand shifting is to imagine a window. For example, let us work on bytes. You can view a byte as:

  0 0 0 0 0 0 0 0 a b c d e f g h 0 0 0 0 0 0 0 0
                 [      B        ]

Now, shifting is just about moving this window:

  0 0 0 0 0 0 0 0 a b c d e f g h 0 0 0 0 0 0 0 0
 [     B >> 8    ]
   [     B >> 7    ]
     [     B >> 6    ]
       [     B >> 5    ]
  0 0 0 0 0 0 0 0 a b c d e f g h 0 0 0 0 0 0 0 0
         [     B >> 4    ]
           [     B >> 3    ]
             [     B >> 2    ]
               [     B >> 1    ]
  0 0 0 0 0 0 0 0 a b c d e f g h 0 0 0 0 0 0 0 0
                   [     B << 1    ]
                     [     B << 2    ]
                       [     B << 3    ]
                         [     B << 4    ]
  0 0 0 0 0 0 0 0 a b c d e f g h 0 0 0 0 0 0 0 0
                           [     B << 5    ]
                             [     B << 6    ]
                               [     B << 7    ]
                                 [     B << 8    ]
  0 0 0 0 0 0 0 0 a b c d e f g h 0 0 0 0 0 0 0 0

If you look at the direction of the arrows, you can think of it as having a fixed window and a moving content... just like your fancy mobile phone touch screen!

So, what is happening in the expression a = (a << k) | (b >> (32-k)) ?

  • a << k selects the 32 - k rightmost bits of a and move them toward the left, creating a space of k 0 on the right side
  • b >> (32-k) selects the k leftmost bits of b and move them toward the right, creating a space of 32 - k 0 on the left side
  • the two are merged together

Getting back to using byte-length bites:

  • Suppose that a is [a7, a6, a5, a4, a3, a2, a1, a0]
  • Suppose that b is [b7, b6, b5. b4, b3, b2, b1, b0]
  • Suppose that k is 3

The number represented is:

// before
 a7 a6 a5 a4 a3 a2 a1 a0 b7 b6 b5 b4 b3 b2 b1 b0
[           a           ]
                        [           b           ]

// after (or so we would like)
 a7 a6 a5 a4 a3 a2 a1 a0 b7 b6 b5 b4 b3 b2 b1 b0
         [           a           ]
                                 [           b           ]

So:

  • a << 3 does actually become a4 a3 a2 a1 a0 0 0 0
  • b >> (8 - 3) becomes 0 0 0 0 0 b7 b6 b5
  • combining with | we get a4 a3 a2 a1 a0 b7 b6 b5

rinse and repeat :)

Matthieu M.
  • 251,718
  • 39
  • 369
  • 642
0

my variant for logical left shift of 128 bit number in little endian environment:

typedef struct { unsigned int component[4]; } vector4; 
vector4 shift_left_logical_128bit_le(vector4 input,unsigned int numbits) {
    vector4 result;
    if(n>=128) {
         result.component[0]=0;
         result.component[1]=0;
         result.component[2]=0;
         result.component[3]=0;
         return r;
    }
    result=input;
    while(numbits>32) {
        numbits-=32;
        result.component[0]=0;
        result.component[1]=result.component[0];
        result.component[2]=result.component[1];
        result.component[3]=result.component[2];
    }
    unsigned long long temp;
    result.component[3]<<=numbits;
    temp=(unsigned long long)result.component[2];
    temp=(temp<<numbits)>>32;
    result.component[3]|=(unsigned int)temp;
    result.component[2]<<=numbits;
    temp=(unsigned long long)result.component[1];
    temp=(temp<<numbits)>>32;
    result.component[2]|=(unsigned int)temp;
    result.component[1]<<=numbits;
    temp=(unsigned long long)result.component[0];
    temp=(temp<<numbits)>>32;
    result.component[1]|=(unsigned int)temp;
    result.component[0]<<=numbits;
    return result;
}
NoAngel
  • 810
  • 2
  • 15
  • 26
0

Note that in the else case k is guaranteed to be 32 or less. So each part of your larger number can actually be shifted by k bits. However, shifting it either left or right makes the k higher/lower bits 0. To shift the whole 128bit number you need to fill these k bits with the bits "shifted out" of the neighboring number.

In the case of a left shift by k, the k lower bits of the higher number need to be filled with the k upper bits of the lower number. to get these upper k bits, we shift that (32bit) number right by 32-k bits and now we got those bits in the right position to fill in the zero k bits from the higher number.

BTW: the code above assumes that an unsigned int is exactly 32 bits. That is not portable.

PeterSom
  • 1,939
  • 16
  • 16
0

To simplify, consider a 16-bit unsigned short, where we store the high and low bytes as unsigned char h, l respectively. To simplify further, let's just shift it left by one bit, to see how that goes.

I'm writing it out as 16 consecutive bits, since that's what we're modelling:

[h7 h6 h5 h4 h3 h2 h1 h0 l7 l6 l5 l4 l3 l2 l1 l0]

so, [h, l] << 1 will be

[h6 h5 h4 h3 h2 h1 h0 l7 l6 l5 l4 l3 l2 l1 l0 0]

(the top bit, h7 has been rotated off the top, and the low bit is filled with zero). Now let's break that back up into h and l ...

[h, l] = [h6 h5 h4 h3 h2 h1 h0 l7 l6 l5 l4 l3 l2 l1 l0 0]
=> h = [h6 h5 h4 h3 h2 h1 h0 l7]
     = (h << 1) | (l >> 7)

etc.

Useless
  • 55,472
  • 5
  • 73
  • 117