Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/spring-mvc/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Binary 查找0数等于1数的第一个位置的位技巧_Binary_Bit Manipulation_X86 64_Bit_Iec10967 - Fatal编程技术网

Binary 查找0数等于1数的第一个位置的位技巧

Binary 查找0数等于1数的第一个位置的位技巧,binary,bit-manipulation,x86-64,bit,iec10967,Binary,Bit Manipulation,X86 64,Bit,Iec10967,假设我有一个32或64位无符号整数 找到最左侧位的索引i的最快方法是什么,以使最左侧i位中的0数等于最左侧i位中的1数? 我在想一些像上面提到的小把戏 我对最新的x86_64处理器感兴趣。这可能与某些处理器支持的指令有关,如POPCNT(计算1的数量)或LZCNT(计算前导0的数量) 如果有帮助的话,可以假设第一位始终具有某个值 示例(16位): 如果整数为 1110010100110110b ^ i 然后i=10,它对应于标记的位置 16位整数的可能(

假设我有一个32或64位无符号整数

找到最左侧位的索引i的最快方法是什么,以使最左侧i位中的0数等于最左侧i位中的1数? 我在想一些像上面提到的小把戏

我对最新的x86_64处理器感兴趣。这可能与某些处理器支持的指令有关,如POPCNT(计算1的数量)或LZCNT(计算前导0的数量)

如果有帮助的话,可以假设第一位始终具有某个值

示例(16位): 如果整数为

1110010100110110b 
         ^ 
         i
然后i=10,它对应于标记的位置

16位整数的可能(缓慢)实现可能是:

mask = 1000000000000000b
pos = 0
count=0
do {
    if(x & mask)
        count++;
    else
        count--;

    pos++;
    x<<=1;
} while(count)

return pos;
mask=10000000000000B
pos=0
计数=0
做{
if(x和掩码)
计数++;
其他的
计数--;
pos++;

我没有任何技巧,但我有一个SIMD技巧

首先是一些观察

  • 将0解释为-1,此问题变为“查找第一个
    i
    ,以便第一个
    i
    位总和为0”
  • 0是偶数,但在此解释下,所有位都有奇数值,这说明
    i
    必须是偶数,并且可以通过2位的块来分析此问题
  • 01和10不改变平衡
将2的组分散到字节后(以下各项均未进行测试)

将00替换为-1,将11替换为1,将01和10替换为0:

__m128i r = _mm_shuffle_epi8(_mm_setr_epi8(-1, 0, 0, 1,  0,0,0,0,0,0,0,0,0,0,0,0),
                             spread);
计算前缀和:

__m128i pfs = _mm_add_epi8(r, _mm_bsrli_si128(r, 1));
pfs = _mm_add_epi8(pfs, _mm_bsrli_si128(pfs, 2));
pfs = _mm_add_epi8(pfs, _mm_bsrli_si128(pfs, 4));
pfs = _mm_add_epi8(pfs, _mm_bsrli_si128(pfs, 8));
查找最高的0:

__m128i iszero = _mm_cmpeq_epi8(pfs, _mm_setzero_si128());
return __builtin_clz(_mm_movemask_epi8(iszero) << 15) * 2;
\uuuum128i iszero=\umm\ucmpeq\uepi8(pfs,\umm\usetzero\uSI128());
返回一个可能的解决方案(对于32位整数)。我不确定是否可以改进/避免使用查找表。这里x是输入整数

//Look-up table of 2^16 elements.
//The y-th is associated with the first 2 bytes y of x.
//If the wanted bit is in y, LUT1[y] is minus the position of the bit
//If the wanted bit is not in y, LUT1[y] is the number of ones in excess in y minus 1 (between 0 and 15)
LUT1 = ....

//Look-up talbe of 16 * 2^16 elements.
//The y-th element is associated to two integers y' and y'' of 4 and 16 bits, respectively.
//y' is the number of excess ones in the first byte of x, minus 1
//y'' is the second byte of x. The table contains the answer to return.
LUT2 = ....

if(LUT1[x>>16] < 0)
    return -LUT1[x>>16];

return LUT2[ (LUT1[x>>16]<<16) | (x & 0xFFFF) ]
//查找2^16个元素的表。
//y-th与x的前2个字节y相关联。
//如果所需位在y中,则LUT1[y]减去该位的位置
//如果所需位不在y中,则LUT1[y]是y中超出的位数减去1(介于0和15之间)
LUT1=。。。。
//查找16*2^16个元素的列表。
//第y个元素分别与4位和16位的两个整数y'和y''相关联。
//y'是x的第一个字节中多余的1的数目,减去1
//y“”是x的第二个字节。该表包含要返回的答案。
LUT2=。。。。
if(LUT1[x>>16]<0)
返回-LUT1[x>>16];
返回LUT2[(LUT1[x>>16]24
if(LUT1[y]<0)
返回-LUT1[y];
y=(LUT1[y]16)和0xFF);
if(LUT2[y]<0)
返回-LUT2[y];
y=(LUT2[y]8)&0xFF);
if(LUT3[y]<0)
返回-LUT3[y];

返回LUT4[(LUT2[y]这是一个使用经典位旋转技术的32位数据解决方案。中间计算需要64位算术和逻辑运算。我必须尽可能地坚持可移植运算。需要实现POSIX函数
ffsll
,以在64-b中查找最低有效位它
long-long
,以及一个自定义函数
rev_bit_duos
,该函数将位duos反转为32位整数。后者可以用特定于平台的位反转内在函数(如ARM平台)替代

基本观察结果是,如果可以提取具有相等数量的0位和1位的位组,则它必须包含偶数位。这意味着我们可以在2位组中检查操作数。我们可以进一步限制自己跟踪每2位是增加(
0b11
),减少(
0b00
)还是保持不变(
0b01
0b10
)位的连续平衡。如果我们使用单独的计数器计算正负变化,4位计数器就足够了,除非输入是
0
0xffffffff
,可以单独处理。根据对问题的评论,这些情况不应该发生。从正变化c中减去负变化计数对于每个2位组,我们可以找到平衡变为零的组。可能有多个这样的位组,我们需要找到第一个

处理过程可以并行化,然后可以用作更改计数器。前缀和可以通过带适当常数的整数乘法计算,这在每个半字节位置提供了必要的移位和加法操作。并行半字节减法的有效方法是众所周知的,同样也有一个众所周知的方法微小地改变为零半字节检测。然后应用POSIX函数
ffsll
查找该半字节的位位置

稍微有点问题的是提取最左边的位组而不是最右边的位组的要求,因为Alan Mycroft的技巧只适用于从右边查找第一个零位。此外,处理最左边位组的前缀和需要使用
mulhi
操作,这可能不容易获得,并且可能更少比标准整数乘法更高效。我通过简单地对原始操作数进行位反转来解决这两个问题

#包括
#包括
#包括
#包括
/*使用经典二进制分区算法的反向位DUO*/
内嵌式uint32修订位双操作系统(uint32修订位a)
{
uint32_t m;

a=(a>>16)|(a>8)&m)|((a4)&m)|((a2)&m)|((a和
i=0
将被禁止,对吗?否则这有点无聊是的:)我必须在1范围内,…,其中size是整数的位数。我不清楚规格。你能提供一个简单的(慢的)吗参考实现您的想法?在我看来,这样一个最左边的位位置并不总是可以找到的,一个简单的32位示例是0xFFFFFE(或者在这种情况下结果是32?),您可以假设这样一个位置总是存在的。换句话说,如果这样一个位置不存在,任何结果都可以。Ref
//Look-up table of 2^16 elements.
//The y-th is associated with the first 2 bytes y of x.
//If the wanted bit is in y, LUT1[y] is minus the position of the bit
//If the wanted bit is not in y, LUT1[y] is the number of ones in excess in y minus 1 (between 0 and 15)
LUT1 = ....

//Look-up talbe of 16 * 2^16 elements.
//The y-th element is associated to two integers y' and y'' of 4 and 16 bits, respectively.
//y' is the number of excess ones in the first byte of x, minus 1
//y'' is the second byte of x. The table contains the answer to return.
LUT2 = ....

if(LUT1[x>>16] < 0)
    return -LUT1[x>>16];

return LUT2[ (LUT1[x>>16]<<16) | (x & 0xFFFF) ]
LUT1 = ... //2^8 elements
LUT2 = ... //8 * 2^8 elements
LUT3 = ... //16 * 2^8 elements
LUT3 = ... //24 * 2^8 elements

y = x>>24
if(LUT1[y] < 0)
    return -LUT1[y];

y = (LUT1[y]<<8) | ((x>>16) & 0xFF);
if(LUT2[y] < 0)
    return -LUT2[y];

y = (LUT2[y]<<8) | ((x>>8) & 0xFF);
if(LUT3[y] < 0)
    return -LUT3[y];

return LUT4[(LUT2[y]<<8) | (x & 0xFF) ];