用于在任意位置从一个int复制N位到另一个int的算法

时间:2009-08-16 00:42:35

标签: c++ algorithm bit-manipulation

我过去几天一直在思考的一个有趣的问题是如何将一个整数的位复制到目标整数中给定位置的另一个整数。因此,例如,给定目标整数0xdeadbeef和源整数0xabcd,想法是获得0xabcdbeef的结果(给定目标位置为16位)或{{ 1}}(给定目标位置为8位)。

由于避免条件或循环的任意限制(允许自己只使用数学/按位运算),我开发了以下函数(C ++)

0xdeabcdef

其中int setbits(int destination, int source, int at, int numbits) { int ones = ((1<<(numbits))-1)<<at; return (ones|destination)^((~source<<at)&ones); } 是应将源位复制到目标号码(0-31)的位置,at是从numbits复制的位数(1- 32)。据我所知,该算法适用于除source = 0和at = 32之外的所有值(由于整数目标整数被源整数覆盖的情况) 1 <&lt;&lt; 32导致1(因为移位环绕)而不是0.

我的问题是:

  1. 这是怎么做到的?是否有任何特别值得注意的算法使用(值得注意的是,我问是否有任何特别有效的技巧可以用来做到这一点)?
  2. 我的算法是否与我认为的算法一样好(也就是说,除了= 0和numbits = 32之外的所有值都有效)?
  3. 与1)相关,有没有办法只使用数学/位运算符?使用条件或循环对所有值的算法都是微不足道的,所以我对此不感兴趣。
  4. 算法设计对我来说通常是一个弱点,所以当我只使用数学/按位运算时,我不知道我的算法是否“尽可能好”。感谢

7 个答案:

答案 0 :(得分:5)

除非你编写汇编程序,否则我认为它不会更有效率。

您可以提高可读性并解决您的溢出问题,从而改变一些小事情:

int setbits2(int destination, int source, int at, int numbits)
{
    // int mask = ((1LL<<numbits)-1)<<at; // 1st aproach
    int mask = ((~0u)>>(sizeof(int)*8-numbits))<<at; // 2nd aproach
    return (destination&~mask)|((source<<at)&mask);
}

更高效的汇编程序版本(VC ++):

// 3rd aproach
#define INT_SIZE 32;
int setbits3(int destination, int source, int at, int numbits)
{ __asm {
    mov ecx, INT_SIZE
    sub ecx, numbits
    or  eax, -1
    shr eax, cl
    mov ecx, at
    shl eax, cl // mask == eax
    mov ebx, eax
    not eax
    and eax, destination
    mov edx, source
    shl edx, cl
    and edx, ebx
    or  eax, edx
}}
  • 第一个方法:在32位架构上更慢
  • 第二个方法:(~0u)和(sizeof(int)* 8)在编译时计算,因此不收取任何费用。
  • 第3个方法:你保存3个操作(内存访问)在汇编程序中编写它,但如果你想让它可移植,你需要编写ifdef。

答案 1 :(得分:3)

我不认为1&lt;&lt; 32包裹(否则,为什么不&lt;&lt; 31也包裹?),而我认为内部模数32应用于第二个运算符,所以1 <&lt; 32&lt; 32实际上等于1 <&lt; 0。另外,请考虑将参数类型从“int”更改为“unsigned int”。要获得“一”的值而不遇到“1&lt;&lt; 32”问题,您可以这样做:

unsigned int ones = (0xffffffff >> (32-numbits)) << at;

我不相信这种操作有任何“标准”方法。我确信还有其他方法可以以不同的方式使用按位运算符来实现相同的结果,但您的算法与任何算法一样好。

尽管如此,可维护性和文档也很重要。您的函数将受益于使用注释记录的算法,特别是解释您如何使用按位异或 - 这很聪明,但乍一看并不容易理解。

答案 2 :(得分:2)

非常好:我尝试了这个替代版本,但你的测试速度提高了约30%:

    int[] bits = new int[] {0,1,3,7,15,31,63,127,255,511,1023
        ,2047,4095,8192,16383,32767,65535,131071,262143,524287
        ,1048575,2097151,4194303,8388607,16777215,33554431,67108863
        ,134217727,268435455,536870911,1073741823,2147483647,-1};

    public int setbits2(int destination, int source, int at, int numbits)
    {
        int ones = bits[numbits + at] & ~bits[at];
        return (destination & ~ones) | ((source << at) & ones);
    }

答案 3 :(得分:1)

广义GRB-fnieto形式......

template <typename T>
T setbits4(T destination, T source, int at, int numbits)
{
    T mask = (((T)-1)>>(sizeof(T)*8-numbits))<<at; // 4th aproach
    return (destination&~mask)|((source<<at)&mask);
}

答案 4 :(得分:0)

我认为它几乎不会更有效率。此外,按位运算比任何代数运算快得多。

答案 5 :(得分:0)

// SET OF FUNCTIONS

//##########    BIT - BIT   
template < typename var_t >     inline  var_t       bit_V           ( uint8_t b )                                               { return var_t(1) << b; }           // Same as usual macros, but this one converts de variable type, so that you can use it in uint8_t to uint64_t for example.
template < typename var_t >     inline  var_t       bit_get         ( const var_t & V , uint8_t b )                             { return V &    bit_V<var_t>(b); }  // Can be used as bool or to get the mask of the bit.
template < typename var_t >     inline  var_t       bit_settled     ( const var_t & V , uint8_t b )                             { return V |    bit_V<var_t>(b); } 
template < typename var_t >     inline  var_t       bit_unsettled   ( const var_t & V , uint8_t b )                             { return V &~   bit_V<var_t>(b); } 
template < typename var_t >     inline  void        bit_set         ( var_t & V , uint8_t b )                                   {        V |=   bit_V<var_t>(b); } 
template < typename var_t >     inline  void        bit_unset       ( var_t & V , uint8_t b )                                   {        V &=  ~bit_V<var_t>(b); } 
template < typename var_t >     inline  void        bit_mod         ( var_t & V , uint8_t b , bool set )                        { if (set) bit_set(V,b); else bit_unset(V,b); } //  compiler will optimize depending on if 'set' is constant.
template < typename var_t >     inline  void        bit_cpy         ( var_t & V , const var_t & S , uint8_t b )                 { var_t t = bit_get(S,b); V |= t; V &~ t; } 
template < typename var_t >     inline  void        bit_cpy         ( var_t & V , const var_t & S , uint8_t bV , uint8_t bM )   { bit_mod(V,bV,bit_get(S,bM)); } 
/// MULTIPLE BITS:
template < typename var_t >     inline  void        bits_set        ( var_t & V , const var_t & S )                                     { V |=  S;  }
template < typename var_t >     inline  void        bits_unset      ( var_t & V , const var_t & S )                                     { V &= ~S; }
/// ONLY WITH UNSIGNED INTS:    'at' parameters are refered to the less significant bit (lsb), starting at 0 index ( a byte would have 7 to 0 bits ).
template < typename var_t >             void        bits_cpy        ( var_t & V , const var_t & S , uint8_t numBits , uint8_t atlsb = 0  )  {   //  I choosed not to make this one inline
                                                                        var_t       mask = (~var_t(0)>>(sizeof(var_t)*8 - numBits))<<atlsb;
                                                                        bits_unset  ( V , mask ) ; 
                                                                        bits_set    ( V , S & mask  ) ; 
                                                                    }
template < typename var_t >             void        bits_cpy        ( var_t & V , const var_t & S , uint8_t numBits , uint8_t atVlsb , uint8_t atSlsb ) {   //  I choosed not to make this one inline
                                                                        bits_cpy ( V , (atVlsb>atSlsb)?(S<<(atVlsb-atSlsb)):(S>>(atSlsb-atVlsb)) , numBits , atVlsb ) ; 
                                                                    }
template < typename var_t >             var_t       bits_cpyd       ( const var_t & V , const var_t & S , uint8_t numBits , uint8_t atlsb = 0  )    { 
                                                                        var_t r = V;
                                                                        bits_cpy    (r,S,numBits,atlsb); 
                                                                        return r;
                                                                    }
template < typename var_t >             var_t       bits_cpyd       ( const var_t & V , const var_t & S , uint8_t numBits , uint8_t atVlsb , uint8_t atSlsb )   {   
                                                                        var_t r = V;
                                                                        bits_cpy    (r,S,numBits,atVlsb,atSlsb); 
                                                                        return r;
                                                                    }

//##########    BIT - BIT   - EXAMPLE OF USE WITH THE MOST RELEVANT FUNCTIONS:
// I used them inside functions, to get/set two variables inside a class, u and c

                void                u_set               ( edrfu_t u )       {           bits_cpy    <uint32_t>  ( CFG       , u         , 8     , 2             ,0              );}
                edrfu_t             u_get               ()                  { return    bits_cpyd   <uint32_t>  ( 0         , CFG       , 8     , 0             ,2              );}
                void                c_set               ( edrfc_t c )       {           bits_cpy    <uint32_t>  ( CFG       , c         , 2     );}
                edrfc_t             c_get               ()                  { return    bits_cpyd   <uint32_t>  ( 0         , CFG       , 2     );}

答案 6 :(得分:0)

uint32_t的 copy_bits(uint32_t dst,uint32_t src,uint8_t end_bit,uint8_t start_bit)

{

uint32_t left, right, mask, result;

if (end_bit <= start_bit)
{
    printf("%s: end_bit:%d shall be greater than start_bit: %d\n", __FUNCTION__, end_bit, start_bit);
    return 0;
}

left   = ~0; // All Fs
right  = ~0;
result = 0;
left  >>= ((sizeof(uint32_t)*8) - end_bit); // Create left half of mask
right <<= start_bit; // Create right half of mask
mask   =  (left & right); // Now you have the mask for specific bits
result = (dst & (~mask)) | (src & (mask));
printf("%s, dst: 0x%08x, src: 0x%08x, end_bit: %d, start_bit: %d, mask: 0x%08x, result: 0x%08x\n",
      __FUNCTION__, dst, src, end_bit, start_bit, mask, result);

return result;

}