用二分法得到最左边的位

问题描述

我正在尝试了解以下代码的工作原理,但我无法理解此处发生的情况:

uint32_t mask[5] = { 0xFFFF0000,0xFF00,0xF0,0b1100,0b10 };
uint32_t shift[5] = { 16,8,4,2,1 };

char first_bit_left_dichotomy(uint32_t M) {
    char pos = 0;
    char i;
    for (i = 0; i <= 4; i++) {
        if (M & mask[i]) { 
            M = M >> shift[i]; 
            pos += shift[i]; 
        }
    }
    return pos;
}

确实,我有两个问题,首先:如果是大小比较,不应该按此顺序创建掩码吗?

uint32_t mask[5] = { 0b100,0xFFFF0000 };

那么,for 循环中的程序是什么?通过我的研究,我了解 &>> 以及它们的按位行为,但这里有什么技巧,因为我猜它只是并且仅与 mask[0] 进行比较,因为它必须具有相同的大小。

解决方法

该算法遵循“分而治之”原则,通过对 或 位模式应用二进制搜索来确定最高有效位是什么。

它基本上将每个周期的机器字减半。这样做的好处在于,您总是在 5 个步骤内计算 MSB,无论您在二分查找中放入什么 32 位模式都具有 O(log2(n)) 特征。

让我们选择两个极端来说明行为并假设单词 0x00000001 作为算法的输入。我们希望它输出 0。基本上发生的是:

0x00000001 & 0xFFFF0000 = 0x00000000
-> We don't shift anything,M=0x00000001,pos=0

0x00000001 & 0xFF00 = 0x0000
-> We don't shift anything,pos=0

0x00000001 & 0xF0 = 0x00
-> We don't shift anything,pos=0

0x00000001 & 0b1100 = 0x00
-> We don't shift anything,pos=0

0x00000001 & 0b10 = 0b0
-> We don't shift anything,pos=0

所以我们在 5 个步骤内得到了结果。想象一下现在从左到右循环尝试同样的事情:需要 31 步才能得到结果。

还有词 0x8FFFFFFF 作为算法的输入需要 5 个步骤才能得到预期的结果 31

0x8FFFFFFF & 0xFFFF0000 = 0x8FFF0000
-> We shift by 16 right,M=0x8FFF,pos=16

0x8FFF & 0xFF00 = 0x8F00
-> We shift by 8 right,M=0x8F,pos=24

0x8F & 0xF0 = 0x80
-> We shift by 4 right,M=0x8,pos=28

0x8 & 0b1100 = 0x8
-> We shift by 2 right,M=0x2,pos=30

0x2 & 0b10 = 0x2
-> We shift by 1 right,M=0x1,pos=31

如您所见,两个极端都让我们完成了这 5 个步骤。由于循环展开、指令的条件执行等,这应该运行得非常快,至少比在循环中从左到右查找 MSB 集要快得多。