How can I perform arithmetic right shift in C in a portable way?

后端 未结 8 1652
挽巷
挽巷 2020-12-19 06:29

We are writing an emulator where we need sign propagating right shift. The emulated system uses 2\'s complement numbers.

I read that the >> operat

相关标签:
8条回答
  • 2020-12-19 07:17

    This function will work no matter the machine definition of 'int', by shifting the absolute value (i.e. without the sign) and then adding the sign:

    int shift(int value, int count)
    {
      return ((value > 0) - (value < 0)) * (abs(value) >> count);
    }
    
    0 讨论(0)
  • 2020-12-19 07:18

    One possible approach is to first perform an unsigned right shift, then sign extend the shifted value based on the value of the most significant bit. Using the fact that when adding two bits a and b, the sum bit is a ^ b and the carry bit is a & b, we can construct sign extension in two ways. As it turns out, using the approach based on the sum bit is more efficient.

    The code below shows the emulation of arithmetic right shift as function arithmetic_right_shift() together with a test framework; T is the integer type you wish to operate on.

    #include <stdio.h>
    #include <stdlib.h>
    #include <limits.h>
    
    #define T int
    #define EXTEND_USING_CARRY_BIT  (1)
    #define EXTEND_USING_SUM_BIT    (2)
    
    #define SIGN_EXTEND_METHOD EXTEND_USING_SUM_BIT
    
    T arithmetic_right_shift (T a, int s)
    {
        unsigned T mask_msb = (unsigned T)1 << (sizeof(T) * CHAR_BIT - 1);
        unsigned T ua = a;
        ua = ua >> s;
        mask_msb = mask_msb >> s;
    #if (SIGN_EXTEND_METHOD == EXTEND_USING_SUM_BIT) 
        return (T)((ua ^ mask_msb) - mask_msb);
    #else // SIGN_EXTEND_METHOD
        return (T)(ua - 2 * (ua & mask_msb));
    #endif // SIGN_EXTEND_METHOD
    }
    
    int sar_ref (int a, int s)
    {
        int res;
        __asm mov eax, dword ptr [a];
        __asm mov ecx, s;
        __asm sar eax, cl;
        __asm mov dword ptr [res], eax;
        return res;
    }
    
    int main (void) 
    {
        unsigned int x;
        int a, s, res, ref;
    
        s = 0;
        do {
            x = 0;
            do {
                a = (int)x;
                res = arithmetic_right_shift (a, s);
                ref = sar_ref (a, s);
                if (ref != res) {
                    printf ("!!!! a=%08x s=%d  res=%08x  ref=%08x\n", 
                            a, s, res, ref);
                    return EXIT_FAILURE;
                }
                x++;
            } while (x);
            s++;
        } while (s < 32);
        return EXIT_SUCCESS;
    }
    
    0 讨论(0)
提交回复
热议问题