将任意位置的 N 位从一个 int 复制到另一个 int 的算法
Posted
技术标签:
【中文标题】将任意位置的 N 位从一个 int 复制到另一个 int 的算法【英文标题】:Algorithm for copying N bits at arbitrary position from one int to another 【发布时间】:2009-08-16 00:42:34 【问题描述】:过去几天我一直在思考的一个有趣问题是如何将一个整数的位复制到目标整数中给定位置的另一个整数中。因此,例如,给定目标整数0xdeadbeef
和源整数0xabcd
,我们的想法是得到0xabcdbeef
(给定16 位目标位置)或0xdeabcdef
(给定目标位置)的结果8位)。
由于避免条件或循环的任意限制(允许自己仅使用数学/按位运算),我开发了以下函数 (C++)
int setbits(int destination, int source, int at, int numbits)
int ones = ((1<<(numbits))-1)<<at;
return (ones|destination)^((~source<<at)&ones);
其中at
是应将源位复制到目标编号 (0-31) 的位置,numbits
是从source
(1-32) 复制的位数。据我所知,由于 1
我的问题是:
-
这通常是如何完成的?是否使用了任何特别值得注意的算法(通过 notable,我想问是否有任何特别有效的技巧可以用来做到这一点)?
我的算法是否像我认为的那样有效(也就是说,适用于除 at = 0 和 numbits = 32 之外的所有值)?
与 1) 相关,有没有办法只使用数学/位运算符来做到这一点?所有值的算法使用条件或循环都很简单,所以我对此不感兴趣。
算法设计对我来说通常是一个弱点,所以我不知道我的算法在仅使用数学/位运算时是否“尽可能好”。谢谢
【问题讨论】:
这很漂亮!我偶尔需要在每个循环都很重要的环境中处理奇怪大小的位串——我一定会把它添加到我的技巧包中。 请注意,我只是做了一些肤浅的测试,所以我不能保证这个方法100%有效 使用 unsigned int 可能会更安全,以避免任何带有符号位的有趣业务。 【参考方案1】:除非你编写汇编程序,否则我认为它不会更有效。
您可以通过更改一些小东西来提高可读性并解决溢出问题:
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 个操作(内存访问),将其写入汇编程序,但如果您想使其可移植,则需要编写 ifdefs。
【讨论】:
在 32 位架构上,这会带来性能损失。他没有要求代码美化器或解决“溢出问题”,如果该功能仅用于 numbits 首先,你有没有看我的第一句话??其次,你从哪里读到这个函数只用于 numbits @fnieto: sizeof(int)/sizeof(destination)/sizeof(source) 也可以工作,不需要 ACE 标头 @GRB:你是对的,在这种情况下,我认为 sizeof 将始终在编译时进行评估。所以我编辑将 ACE_SIZEOF_INT 更改为 sizeof(int)。如果有人确认这不依赖于编译器,无论如何都会很好。在第一次查看标准时,我没有找到它。 5.19/1 保证 sizeof 是一个常量表达式(因此我认为这意味着它是在编译时评估的,参见 3.6.2/1)【参考方案2】:我不认为 1
unsigned int ones = (0xffffffff >> (32-numbits)) << at;
我不相信这种操作有任何“标准”方法。我敢肯定还有其他方法可以以不同的方式使用按位运算符来实现相同的结果,但是您的算法和其他算法一样好。
话虽如此,可维护性和文档也很重要。您的函数将受益于带有注释的算法,尤其是解释您如何使用按位 XOR 的方法——这很聪明,但乍一看并不容易理解。
【讨论】:
这用numbits == 0
代替numbits == 32
的问题,但由于这并没有多大意义,所以它可以从函数的允许域中排除。
你是对的,“换行”可能是错误的选择。我的意思是指执行的内部模数运算(您提到的)。
此解决方案依赖于架构。将 0xffffffff 替换为 ~0(无成本,编译时间),将 32 替换为为您要支持的架构定义的宏 INT_SIZE。
~0 确实很漂亮。这是“ones”的计算,它甚至无需使用 INT_SIZE 即可实现可移植性:~(~0
恐怕不是@Todd,当你这样做时 (~0
【参考方案3】:
非常好:我尝试了这个替代版本,但你的测试速度快了大约 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);
【讨论】:
该表在十六进制中可能更有意义【参考方案4】:广义 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);
【讨论】:
【参考方案5】:我认为它几乎没有比这更有效的了。此外,按位运算比任何代数运算都要快。
【讨论】:
我认为你在这两点上都错了。它可以更有效(请参阅我的答案),并且至少以与我知道的每个拱门上的按位运算相同的速度执行加法和减法。【参考方案6】: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;
【讨论】:
如果你解释清楚,你的答案会更有意义。【参考方案7】:// 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 );
【讨论】:
已经过测试,但欢迎任何改进。最后有关于内联或不“大”功能的任何建议吗? 关于性能,我觉得和之前建议的差不多。也许 bits_cpy 更快一点,因为它只需要四个操作(除了掩码),而不是五个。以上是关于将任意位置的 N 位从一个 int 复制到另一个 int 的算法的主要内容,如果未能解决你的问题,请参考以下文章