如果我有一个整数n,我想知道最高位的位置(也就是说,如果最低位在右边,我想知道最左边位的位置是1),找出最快/最有效的方法是什么?
我知道POSIX在strings.h中支持ffs()
方法来查找第一个设置位,但似乎没有相应的fls()
方法。
是否有一些非常明显的方法可以解决这个问题?
如果您无法使用POSIX功能实现可移植性,那该怎么办?
编辑:如何在32位和64位架构上运行的解决方案(许多代码清单似乎只能在32位整数上运行)。
答案 0 :(得分:57)
-- Built-in Function: int __builtin_clz (unsigned int x) Returns the number of leading 0-bits in X, starting at the most significant bit position. If X is 0, the result is undefined. -- Built-in Function: int __builtin_clzl (unsigned long) Similar to `__builtin_clz', except the argument type is `unsigned long'. -- Built-in Function: int __builtin_clzll (unsigned long long) Similar to `__builtin_clz', except the argument type is `unsigned long long'.
我希望它们能够被翻译成当前平台的合理效率,无论是那种花哨的比特算法还是单一指令。
如果输入 为零,则有用的技巧是__builtin_clz(x | 1)
:无条件地设置低位而不修改任何其他位,使得0
的输出为x=0
,不改变任何其他输入的输出。
为避免需要这样做,您的另一个选项是特定于平台的内在函数,如ARM GCC的__clz
(不需要头),或者支持_lzcnt_u32
指令的CPU上的x86 lzcnt
。 (注意lzcnt
在较旧的CPU上解码为bsr
而不是故障,这为非零输入提供了31-lzcnt。)
遗憾的是,无法在非x86平台上轻松利用各种CLZ指令,这些指令将input = 0的结果定义为32或64(根据操作数宽度)。 x86的lzcnt
也会这样做,而bsr
产生一个比特索引,编译器必须翻转,除非你使用31-__builtin_clz(x)
。
(“未定义的结果”不是C未定义的行为,只是一个未定义的值。它实际上是指令运行时目标寄存器中的任何内容.AMD记录了这一点,英特尔没有,但英特尔的CPU做了实现那种行为。但是不是以前在你分配的C变量中的任何东西,当gcc将C变成asm时,通常情况并不常见。参见Why does breaking the "output dependency" of LZCNT matter?)
答案 1 :(得分:40)
假设您使用x86并使用一些内联汇编程序进行游戏,则Intel会提供BSR
指令(“位扫描反向”)。它是一些 x86s上的fast(在其他人上进行微编码)。从手册:
在源操作数中搜索最重要的集合 位(1位)。如果最显着1 找到位,存储其位索引 在目标操作数中。源操作数可以是a 寄存器或存储位置;该 目标操作数是一个寄存器。该 位索引是一个无符号偏移量 源操作数的第0位。如果 内容源操作数为0,即 目标操作数的内容是 未定义。
(如果你在PowerPC上有类似的cntlz
(“计数前导零”)指令。)
gcc的示例代码:
#include <iostream>
int main (int,char**)
{
int n=1;
for (;;++n) {
int msb;
asm("bsrl %1,%0" : "=r"(msb) : "r"(n));
std::cout << n << " : " << msb << std::endl;
}
return 0;
}
另请参阅此inline assembler tutorial,其中显示(第9.4节)它比循环代码快得多。
答案 2 :(得分:35)
由于2 ^ N是仅设置了第N位的整数(1 http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogObvious 这种“显而易见”的算法可能对每个人都不透明,但是当你意识到代码重复向右移动一位直到最左边的位被移开(注意C将任何非零值视为真)和返回班次的数量,这是完全合理的。它还意味着即使设置了多个位也能工作 - 结果始终是最重要的位。 如果向下滚动该页面,则会有更快,更复杂的变化。但是,如果你知道你正在处理具有许多前导零的数字,那么天真的方法可能提供可接受的速度,因为C中的位移相当快,而简单的算法不需要索引数组。 注意:使用64位值时,要非常小心使用超智能算法;其中许多只能正确处理32位值。unsigned int v;
unsigned r = 0;
while (v >>= 1) {
r++;
}
答案 3 :(得分:16)
这应该是闪电般的:
int msb(unsigned int v) {
static const int pos[32] = {0, 1, 28, 2, 29, 14, 24, 3,
30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19,
16, 7, 26, 12, 18, 6, 11, 5, 10, 9};
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v = (v >> 1) + 1;
return pos[(v * 0x077CB531UL) >> 27];
}
答案 4 :(得分:13)
这有点像找一种整数日志。有点蠢蠢欲动,但我已经为此制作了自己的工具。当然,目标是速度。
我的意识是CPU已经有一个自动位检测器,用于整数到浮点转换!所以使用它。
double ff=(double)(v|1);
return ((*(1+(uint32_t *)&ff))>>20)-1023; // assumes x86 endianness
此版本将值转换为double,然后读取指数,该指数告诉您该位的位置。花式移位和减法是从IEEE值中提取适当的部分。
使用浮点数稍微快一些,但浮点数只能给你前24位的位置,因为它的精度较低。
为了安全地执行此操作,在C ++或C中没有未定义的行为,请使用memcpy
而不是指针转换来进行类型惩罚。编译器知道如何有效地内联它。
// static_assert(sizeof(double) == 2 * sizeof(uint32_t), "double isn't 8-byte IEEE binary64");
// and also static_assert something about FLT_ENDIAN?
double ff=(double)(v|1);
uint32_t tmp;
memcpy(&tmp, ((const char*)&ff)+sizeof(uint32_t), sizeof(uint32_t));
return (tmp>>20)-1023;
或者在C99及更高版本中,使用union {double d; uint32_t u[2];};
。但请注意,在C ++中,联合类型双关语只在某些编译器上作为扩展支持,而不是在ISO C ++中。
这通常比前导零计数指令的特定于平台的内在要慢,但便携式ISO C没有这样的功能。一些CPU也缺少前导零计数指令,但其中一些可以有效地将整数转换为double
。尽管如此(例如在PowerPC上它需要存储/重新加载并且通常会导致加载命中存储停顿),将FP位模式返回到整数的类型 - 可能会很慢(例如,在PowerPC上)。
此算法可能对SIMD实现有用,因为较少的CPU具有SIMD lzcnt
。 x86只有这样的指令with AVX512CD
答案 5 :(得分:9)
Kaz Kylheku
我基于63位数字(gcc x86_64上的long long类型)对两种方法进行了基准测试,远离符号位。
(我碰巧需要这个&#34;找到最高位&#34;对于某些东西,你看。)
我实现了数据驱动的二进制搜索(紧密基于上述答案之一)。我还手动实现了一个完全展开的决策树,它只是具有即时操作数的代码。没有循环,没有表格。
决策树(highest_bit_unrolled)的基准测试速度提高了69%,除了n = 0的情况,二元搜索有明确的测试。
针对0案例的二元搜索特殊测试仅比决策树快48%,而决策树没有特殊测试。
编译器,机器:( GCC 4.5.2,-O3,x86-64,2867 Mhz Intel Core i5)。
int highest_bit_unrolled(long long n)
{
if (n & 0x7FFFFFFF00000000) {
if (n & 0x7FFF000000000000) {
if (n & 0x7F00000000000000) {
if (n & 0x7000000000000000) {
if (n & 0x4000000000000000)
return 63;
else
return (n & 0x2000000000000000) ? 62 : 61;
} else {
if (n & 0x0C00000000000000)
return (n & 0x0800000000000000) ? 60 : 59;
else
return (n & 0x0200000000000000) ? 58 : 57;
}
} else {
if (n & 0x00F0000000000000) {
if (n & 0x00C0000000000000)
return (n & 0x0080000000000000) ? 56 : 55;
else
return (n & 0x0020000000000000) ? 54 : 53;
} else {
if (n & 0x000C000000000000)
return (n & 0x0008000000000000) ? 52 : 51;
else
return (n & 0x0002000000000000) ? 50 : 49;
}
}
} else {
if (n & 0x0000FF0000000000) {
if (n & 0x0000F00000000000) {
if (n & 0x0000C00000000000)
return (n & 0x0000800000000000) ? 48 : 47;
else
return (n & 0x0000200000000000) ? 46 : 45;
} else {
if (n & 0x00000C0000000000)
return (n & 0x0000080000000000) ? 44 : 43;
else
return (n & 0x0000020000000000) ? 42 : 41;
}
} else {
if (n & 0x000000F000000000) {
if (n & 0x000000C000000000)
return (n & 0x0000008000000000) ? 40 : 39;
else
return (n & 0x0000002000000000) ? 38 : 37;
} else {
if (n & 0x0000000C00000000)
return (n & 0x0000000800000000) ? 36 : 35;
else
return (n & 0x0000000200000000) ? 34 : 33;
}
}
}
} else {
if (n & 0x00000000FFFF0000) {
if (n & 0x00000000FF000000) {
if (n & 0x00000000F0000000) {
if (n & 0x00000000C0000000)
return (n & 0x0000000080000000) ? 32 : 31;
else
return (n & 0x0000000020000000) ? 30 : 29;
} else {
if (n & 0x000000000C000000)
return (n & 0x0000000008000000) ? 28 : 27;
else
return (n & 0x0000000002000000) ? 26 : 25;
}
} else {
if (n & 0x0000000000F00000) {
if (n & 0x0000000000C00000)
return (n & 0x0000000000800000) ? 24 : 23;
else
return (n & 0x0000000000200000) ? 22 : 21;
} else {
if (n & 0x00000000000C0000)
return (n & 0x0000000000080000) ? 20 : 19;
else
return (n & 0x0000000000020000) ? 18 : 17;
}
}
} else {
if (n & 0x000000000000FF00) {
if (n & 0x000000000000F000) {
if (n & 0x000000000000C000)
return (n & 0x0000000000008000) ? 16 : 15;
else
return (n & 0x0000000000002000) ? 14 : 13;
} else {
if (n & 0x0000000000000C00)
return (n & 0x0000000000000800) ? 12 : 11;
else
return (n & 0x0000000000000200) ? 10 : 9;
}
} else {
if (n & 0x00000000000000F0) {
if (n & 0x00000000000000C0)
return (n & 0x0000000000000080) ? 8 : 7;
else
return (n & 0x0000000000000020) ? 6 : 5;
} else {
if (n & 0x000000000000000C)
return (n & 0x0000000000000008) ? 4 : 3;
else
return (n & 0x0000000000000002) ? 2 : (n ? 1 : 0);
}
}
}
}
}
int highest_bit(long long n)
{
const long long mask[] = {
0x000000007FFFFFFF,
0x000000000000FFFF,
0x00000000000000FF,
0x000000000000000F,
0x0000000000000003,
0x0000000000000001
};
int hi = 64;
int lo = 0;
int i = 0;
if (n == 0)
return 0;
for (i = 0; i < sizeof mask / sizeof mask[0]; i++) {
int mi = lo + (hi - lo) / 2;
if ((n >> mi) != 0)
lo = mi;
else if ((n & (mask[i] << lo)) != 0)
hi = mi;
}
return lo + 1;
}
快速而肮脏的测试程序:
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
int highest_bit_unrolled(long long n);
int highest_bit(long long n);
main(int argc, char **argv)
{
long long n = strtoull(argv[1], NULL, 0);
int b1, b2;
long i;
clock_t start = clock(), mid, end;
for (i = 0; i < 1000000000; i++)
b1 = highest_bit_unrolled(n);
mid = clock();
for (i = 0; i < 1000000000; i++)
b2 = highest_bit(n);
end = clock();
printf("highest bit of 0x%llx/%lld = %d, %d\n", n, n, b1, b2);
printf("time1 = %d\n", (int) (mid - start));
printf("time2 = %d\n", (int) (end - mid));
return 0;
}
仅使用-O2,差异会变大。决策树的速度快了近四倍。
我还针对天真的位移代码进行了基准测试:
int highest_bit_shift(long long n)
{
int i = 0;
for (; n; n >>= 1, i++)
; /* empty */
return i;
}
这对于小数字来说只是快速的,正如人们所期望的那样。在确定n == 1的最高位为1时,它的基准测试速度提高了80%以上。但是,63位空间中随机选择的数字的一半具有第63位设置!
在输入0x3FFFFFFFFFFFFFFF上,决策树版本比1更快,并且显示比移位器快1120%(12.2倍)。
我还将基于GCC内置函数对决策树进行基准测试,并尝试混合输入而不是重复相同的数字。可能会有一些粘滞的分支预测正在进行,也许还有一些不切实际的缓存方案会使人工重复更快。
答案 6 :(得分:7)
怎么样?
int highest_bit(unsigned int a) {
int count;
std::frexp(a, &count);
return count - 1;
}
答案 7 :(得分:6)
虽然如果我绝对需要最好的性能(例如编写某种涉及位板的棋盘游戏AI),我可能只会使用这种方法,但最有效的解决方案是使用内联ASM。有关具有解释的代码,请参阅this blog post的“优化”部分。
[...],
bsrl
汇编指令计算最高位的位置。因此,我们可以使用此asm
语句:asm ("bsrl %1, %0" : "=r" (position) : "r" (number));
答案 8 :(得分:5)
unsigned int
msb32(register unsigned int x)
{
x |= (x >> 1);
x |= (x >> 2);
x |= (x >> 4);
x |= (x >> 8);
x |= (x >> 16);
return(x & ~(x >> 1));
}
1个寄存器,13个指令。信不信由你,这通常比上面提到的BSR指令更快,它在线性时间内运行。这是对数时间。
答案 9 :(得分:5)
以下是本页目前给出的算法的一些(简单)基准测试......
尚未对unsigned int的所有输入进行测试;所以在盲目使用之前先检查一下;)
在我的机器上clz(__ builtin_clz)和asm工作得最好。 asm似乎比clz更快......但它可能是由于简单的基准......
//////// go.c ///////////////////////////////
// compile with: gcc go.c -o go -lm
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
/***************** math ********************/
#define POS_OF_HIGHESTBITmath(a) /* 0th position is the Least-Signif-Bit */ \
((unsigned) log2(a)) /* thus: do not use if a <= 0 */
#define NUM_OF_HIGHESTBITmath(a) ((a) \
? (1U << POS_OF_HIGHESTBITmath(a)) \
: 0)
/***************** clz ********************/
unsigned NUM_BITS_U = ((sizeof(unsigned) << 3) - 1);
#define POS_OF_HIGHESTBITclz(a) (NUM_BITS_U - __builtin_clz(a)) /* only works for a != 0 */
#define NUM_OF_HIGHESTBITclz(a) ((a) \
? (1U << POS_OF_HIGHESTBITclz(a)) \
: 0)
/***************** i2f ********************/
double FF;
#define POS_OF_HIGHESTBITi2f(a) (FF = (double)(ui|1), ((*(1+(unsigned*)&FF))>>20)-1023)
#define NUM_OF_HIGHESTBITi2f(a) ((a) \
? (1U << POS_OF_HIGHESTBITi2f(a)) \
: 0)
/***************** asm ********************/
unsigned OUT;
#define POS_OF_HIGHESTBITasm(a) (({asm("bsrl %1,%0" : "=r"(OUT) : "r"(a));}), OUT)
#define NUM_OF_HIGHESTBITasm(a) ((a) \
? (1U << POS_OF_HIGHESTBITasm(a)) \
: 0)
/***************** bitshift1 ********************/
#define NUM_OF_HIGHESTBITbitshift1(a) (({ \
OUT = a; \
OUT |= (OUT >> 1); \
OUT |= (OUT >> 2); \
OUT |= (OUT >> 4); \
OUT |= (OUT >> 8); \
OUT |= (OUT >> 16); \
}), (OUT & ~(OUT >> 1))) \
/***************** bitshift2 ********************/
int POS[32] = {0, 1, 28, 2, 29, 14, 24, 3,
30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19,
16, 7, 26, 12, 18, 6, 11, 5, 10, 9};
#define POS_OF_HIGHESTBITbitshift2(a) (({ \
OUT = a; \
OUT |= OUT >> 1; \
OUT |= OUT >> 2; \
OUT |= OUT >> 4; \
OUT |= OUT >> 8; \
OUT |= OUT >> 16; \
OUT = (OUT >> 1) + 1; \
}), POS[(OUT * 0x077CB531UL) >> 27])
#define NUM_OF_HIGHESTBITbitshift2(a) ((a) \
? (1U << POS_OF_HIGHESTBITbitshift2(a)) \
: 0)
#define LOOPS 100000000U
int main()
{
time_t start, end;
unsigned ui;
unsigned n;
/********* Checking the first few unsigned values (you'll need to check all if you want to use an algorithm here) **************/
printf("math\n");
for (ui = 0U; ui < 18; ++ui)
printf("%i\t%i\n", ui, NUM_OF_HIGHESTBITmath(ui));
printf("\n\n");
printf("clz\n");
for (ui = 0U; ui < 18U; ++ui)
printf("%i\t%i\n", ui, NUM_OF_HIGHESTBITclz(ui));
printf("\n\n");
printf("i2f\n");
for (ui = 0U; ui < 18U; ++ui)
printf("%i\t%i\n", ui, NUM_OF_HIGHESTBITi2f(ui));
printf("\n\n");
printf("asm\n");
for (ui = 0U; ui < 18U; ++ui) {
printf("%i\t%i\n", ui, NUM_OF_HIGHESTBITasm(ui));
}
printf("\n\n");
printf("bitshift1\n");
for (ui = 0U; ui < 18U; ++ui) {
printf("%i\t%i\n", ui, NUM_OF_HIGHESTBITbitshift1(ui));
}
printf("\n\n");
printf("bitshift2\n");
for (ui = 0U; ui < 18U; ++ui) {
printf("%i\t%i\n", ui, NUM_OF_HIGHESTBITbitshift2(ui));
}
printf("\n\nPlease wait...\n\n");
/************************* Simple clock() benchmark ******************/
start = clock();
for (ui = 0; ui < LOOPS; ++ui)
n = NUM_OF_HIGHESTBITmath(ui);
end = clock();
printf("math:\t%e\n", (double)(end-start)/CLOCKS_PER_SEC);
start = clock();
for (ui = 0; ui < LOOPS; ++ui)
n = NUM_OF_HIGHESTBITclz(ui);
end = clock();
printf("clz:\t%e\n", (double)(end-start)/CLOCKS_PER_SEC);
start = clock();
for (ui = 0; ui < LOOPS; ++ui)
n = NUM_OF_HIGHESTBITi2f(ui);
end = clock();
printf("i2f:\t%e\n", (double)(end-start)/CLOCKS_PER_SEC);
start = clock();
for (ui = 0; ui < LOOPS; ++ui)
n = NUM_OF_HIGHESTBITasm(ui);
end = clock();
printf("asm:\t%e\n", (double)(end-start)/CLOCKS_PER_SEC);
start = clock();
for (ui = 0; ui < LOOPS; ++ui)
n = NUM_OF_HIGHESTBITbitshift1(ui);
end = clock();
printf("bitshift1:\t%e\n", (double)(end-start)/CLOCKS_PER_SEC);
start = clock();
for (ui = 0; ui < LOOPS; ++ui)
n = NUM_OF_HIGHESTBITbitshift2(ui);
end = clock();
printf("bitshift2\t%e\n", (double)(end-start)/CLOCKS_PER_SEC);
printf("\nThe lower, the better. Take note that a negative exponent is good! ;)\n");
return EXIT_SUCCESS;
}
答案 10 :(得分:4)
那是某种二进制搜索,它适用于各种(无符号!)整数类型
#include <climits>
#define UINT (unsigned int)
#define UINT_BIT (CHAR_BIT*sizeof(UINT))
int msb(UINT x)
{
if(0 == x)
return -1;
int c = 0;
for(UINT i=UINT_BIT>>1; 0<i; i>>=1)
if(static_cast<UINT>(x >> i))
{
x >>= i;
c |= i;
}
return c;
}
完成:
#include <climits>
#define UINT unsigned int
#define UINT_BIT (CHAR_BIT*sizeof(UINT))
int lsb(UINT x)
{
if(0 == x)
return -1;
int c = UINT_BIT-1;
for(UINT i=UINT_BIT>>1; 0<i; i>>=1)
if(static_cast<UINT>(x << i))
{
x <<= i;
c ^= i;
}
return c;
}
答案 11 :(得分:4)
我需要一个例程来执行此操作,在搜索网页(并找到此页面)之前,我想出了一个基于二分搜索的自己的解决方案。虽然我相信有人之前已经这样做了!它运行在一个恒定的时间,并且比发布的“明显”解决方案更快,虽然我没有做出任何好的声明,只是为了兴趣发布它。
int highest_bit(unsigned int a) {
static const unsigned int maskv[] = { 0xffff, 0xff, 0xf, 0x3, 0x1 };
const unsigned int *mask = maskv;
int l, h;
if (a == 0) return -1;
l = 0;
h = 32;
do {
int m = l + (h - l) / 2;
if ((a >> m) != 0) l = m;
else if ((a & (*mask << l)) != 0) h = m;
mask++;
} while (l < h - 1);
return l;
}
答案 12 :(得分:3)
正如上面的答案所指出的,有很多方法可以确定最重要的位。但是,正如还指出的那样,这些方法可能是32位或64位寄存器的唯一方法。 stanford.edu bithacks page提供适用于32位和64位计算的解决方案。通过一些工作,它们可以结合起来,为获得MSB提供可靠的跨架构方法。我到达的解决方案编译/工作在64&amp; 32位计算机是:
#if defined(__LP64__) || defined(_LP64)
# define BUILD_64 1
#endif
#include <stdio.h>
#include <stdint.h> /* for uint32_t */
/* CHAR_BIT (or include limits.h) */
#ifndef CHAR_BIT
#define CHAR_BIT 8
#endif /* CHAR_BIT */
/*
* Find the log base 2 of an integer with the MSB N set in O(N)
* operations. (on 64bit & 32bit architectures)
*/
int
getmsb (uint32_t word)
{
int r = 0;
if (word < 1)
return 0;
#ifdef BUILD_64
union { uint32_t u[2]; double d; } t; // temp
t.u[__FLOAT_WORD_ORDER==LITTLE_ENDIAN] = 0x43300000;
t.u[__FLOAT_WORD_ORDER!=LITTLE_ENDIAN] = word;
t.d -= 4503599627370496.0;
r = (t.u[__FLOAT_WORD_ORDER==LITTLE_ENDIAN] >> 20) - 0x3FF;
#else
while (word >>= 1)
{
r++;
}
#endif /* BUILD_64 */
return r;
}
答案 13 :(得分:3)
c99已经给了我们log2
。这消除了您在此页面上看到的所有特殊酱log2
实现的需要。您可以使用标准的log2
实现,如下所示:
const auto n = 13UL;
const auto Index = (unsigned long)log2(n);
printf("MSB is: %u\n", Index); // Prints 3 (zero offset)
n
0UL
也需要加以防范,因为:
返回-∞并引发FE_DIVBYZERO
我写了一个例子,在这里任意设置Index
到ULONG_MAX
的检查:https://ideone.com/u26vsi
visual-studio的ephemient's gcc only answer推论是:
const auto n = 13UL;
unsigned long Index;
_BitScanReverse(&Index, n);
printf("MSB is: %u\n", Index); // Prints 3 (zero offset)
The documentation for _BitScanReverse
声明Index
是:
加载第一个设置位(1)的位位置
在实践中,我发现如果n
0UL
n
Index
is set to 0UL
,1UL
n
0UL
log2
。但是Index
ULONG_MAX
{{1}}的文档中唯一保证的是返回:
如果没有找到设置位,则0
因此,与上面优选的{{1}}实现类似,在这种情况下应该检查返回,将{{1}}设置为标记值。我在这里再次写了一个使用{{1}}作为此标志值的示例:http://rextester.com/GCU61409
答案 14 :(得分:3)
使用逐次逼近的C语言版本:
unsigned int getMsb(unsigned int n)
{
unsigned int msb = sizeof(n) * 4;
unsigned int step = msb;
while (step > 1)
{
step /=2;
if (n>>msb)
msb += step;
else
msb -= step;
}
if (n>>msb)
msb++;
return (msb - 1);
}
优点:无论提供的数量如何,运行时间都是恒定的,因为循环次数始终相同。 (使用“unsigned int”时4个循环)
答案 15 :(得分:2)
扩展Josh的基准...... 一个可以改善clz如下
/***************** clz2 ********************/
#define NUM_OF_HIGHESTBITclz2(a) ((a) \
? (((1U) << (sizeof(unsigned)*8-1)) >> __builtin_clz(a)) \
: 0)
关于asm:注意有bsr和bsrl(这是“长”版本)。正常的可能会快一点。
答案 16 :(得分:2)
考虑按位运算符。
我第一次误解了这个问题。您应该生成一个最左边的位设置的int(其他为零)。假设cmp设置为该值:
position = sizeof(int)*8
while(!(n & cmp)){
n <<=1;
position--;
}
答案 17 :(得分:1)
因为它是“另一种”方法所以把它放进去,似乎与已经给出的其他方法不同。
如果-1
则返回x==0
,否则floor( log2(x))
(最多结果31)
从32位减少到4位问题,然后使用表格。也许不优雅,但务实。
由于可移植性问题,我不想使用__builtin_clz
时使用此功能。
为了使其更紧凑,可以使用循环来减少,每次增加4到r,最多7次迭代。或者某些混合,例如(对于64位):循环减少到8,测试减少到4。
int log2floor( unsigned x ){
static const signed char wtab[16] = {-1,0,1,1, 2,2,2,2, 3,3,3,3,3,3,3,3};
int r = 0;
unsigned xk = x >> 16;
if( xk != 0 ){
r = 16;
x = xk;
}
// x is 0 .. 0xFFFF
xk = x >> 8;
if( xk != 0){
r += 8;
x = xk;
}
// x is 0 .. 0xFF
xk = x >> 4;
if( xk != 0){
r += 4;
x = xk;
}
// now x is 0..15; x=0 only if originally zero.
return r + wtab[x];
}
答案 18 :(得分:1)
请注意,您要做的是计算整数的整数log2,
#include <stdio.h>
#include <stdlib.h>
unsigned int
Log2(unsigned long x)
{
unsigned long n = x;
int bits = sizeof(x)*8;
int step = 1; int k=0;
for( step = 1; step < bits; ) {
n |= (n >> step);
step *= 2; ++k;
}
//printf("%ld %ld\n",x, (x - (n >> 1)) );
return(x - (n >> 1));
}
注意您可以尝试一次搜索多于1位。
unsigned int
Log2_a(unsigned long x)
{
unsigned long n = x;
int bits = sizeof(x)*8;
int step = 1;
int step2 = 0;
//observe that you can move 8 bits at a time, and there is a pattern...
//if( x>1<<step2+8 ) { step2+=8;
//if( x>1<<step2+8 ) { step2+=8;
//if( x>1<<step2+8 ) { step2+=8;
//}
//}
//}
for( step2=0; x>1L<<step2+8; ) {
step2+=8;
}
//printf("step2 %d\n",step2);
for( step = 0; x>1L<<(step+step2); ) {
step+=1;
//printf("step %d\n",step+step2);
}
printf("log2(%ld) %d\n",x,step+step2);
return(step+step2);
}
此方法使用二进制搜索
unsigned int
Log2_b(unsigned long x)
{
unsigned long n = x;
unsigned int bits = sizeof(x)*8;
unsigned int hbit = bits-1;
unsigned int lbit = 0;
unsigned long guess = bits/2;
int found = 0;
while ( hbit-lbit>1 ) {
//printf("log2(%ld) %d<%d<%d\n",x,lbit,guess,hbit);
//when value between guess..lbit
if( (x<=(1L<<guess)) ) {
//printf("%ld < 1<<%d %ld\n",x,guess,1L<<guess);
hbit=guess;
guess=(hbit+lbit)/2;
//printf("log2(%ld) %d<%d<%d\n",x,lbit,guess,hbit);
}
//when value between hbit..guess
//else
if( (x>(1L<<guess)) ) {
//printf("%ld > 1<<%d %ld\n",x,guess,1L<<guess);
lbit=guess;
guess=(hbit+lbit)/2;
//printf("log2(%ld) %d<%d<%d\n",x,lbit,guess,hbit);
}
}
if( (x>(1L<<guess)) ) ++guess;
printf("log2(x%ld)=r%d\n",x,guess);
return(guess);
}
另一种二元搜索方法,可能更具可读性
unsigned int
Log2_c(unsigned long x)
{
unsigned long v = x;
unsigned int bits = sizeof(x)*8;
unsigned int step = bits;
unsigned int res = 0;
for( step = bits/2; step>0; )
{
//printf("log2(%ld) v %d >> step %d = %ld\n",x,v,step,v>>step);
while ( v>>step ) {
v>>=step;
res+=step;
//printf("log2(%ld) step %d res %d v>>step %ld\n",x,step,res,v);
}
step /= 2;
}
if( (x>(1L<<res)) ) ++res;
printf("log2(x%ld)=r%ld\n",x,res);
return(res);
}
因为你想测试这些,
int main()
{
unsigned long int x = 3;
for( x=2; x<1000000000; x*=2 ) {
//printf("x %ld, x+1 %ld, log2(x+1) %d\n",x,x+1,Log2(x+1));
printf("x %ld, x+1 %ld, log2_a(x+1) %d\n",x,x+1,Log2_a(x+1));
printf("x %ld, x+1 %ld, log2_b(x+1) %d\n",x,x+1,Log2_b(x+1));
printf("x %ld, x+1 %ld, log2_c(x+1) %d\n",x,x+1,Log2_c(x+1));
}
return(0);
}
答案 19 :(得分:1)
我知道这个问题很老,但我自己实现了 msb()功能, 我发现此处和其他网站上提供的大多数解决方案不一定是效率最高的 - 至少对于我个人的效率定义(另请参阅下面的 更新 )。原因如下:
大多数解决方案(特别是那些采用某种二分搜索方案或从右到左进行线性扫描的天真方法)似乎忽略了这样一个事实:对于任意二进制数,没有很多从一个非常开始的长序列的零。事实上,对于任何位宽,所有整数的一半以 1 开头,其中四分之一以 01 开头。 看看我在哪里?我的论点是线性扫描从最高有效位开始到最低有效位(从左到右)并不像乍一看那样“线性”。
可以显示 1 ,对于任何位宽,需要测试的平均位数最多为2.这转换为摊销 O(1) 相对于位数(!)的时间复杂度。
当然,最糟糕的情况仍然是 O(n) ,比 O(log(n))更糟糕你可以使用类似二分搜索的方法,但由于最糟糕的情况很少,对于大多数应用程序来说它们可以忽略不计( 更新 :不完全:那里可能很少,但它们可能很有可能发生 - 请参阅下面的 更新 。
这是我提出的“天真”方法,至少在我的机器上击败了大多数其他方法(32位整数的二进制搜索方案总是需要 log 2 (32)= 5个步骤,而这个愚蠢的算法平均需要少于2个 - 抱歉这是C ++而不是纯C:
template <typename T>
auto msb(T n) -> int
{
static_assert(std::is_integral<T>::value && !std::is_signed<T>::value,
"msb<T>(): T must be an unsigned integral type.");
for (T i = std::numeric_limits<T>::digits - 1, mask = 1 << i; i >= 0; --i, mask >>= 1)
{
if ((n & mask) != 0)
return i;
}
return 0;
}
更新 :虽然我在这里写的对于任意整数是完全正确的,其中每个位组合都是同样可能的(我的速度测试)简单地测量确定所有 32位整数的MSB所花费的时间长度,现实整数,这个函数将被调用,通常遵循不同的模式:在我的代码中,例如,此函数用于确定对象大小是2的幂,还是查找大于或等于对象大小的下一个2的幂。
我的猜测是,大多数使用MSB的应用程序涉及的数字远小于整数可以表示的最大数量(对象大小很少使用 size_t 中的所有位)。在这种情况下,我的解决方案实际上比二进制搜索方法表现更差 - 因此后者应该是首选,即使我的解决方案将通过所有整数更快地循环。
TL; DR:现实整数可能偏向于这种简单算法的最坏情况,这最终会使其表现更差 - 尽管它已经摊销 O(1) 用于真正的任意整数。
1 这个论点是这样的(草稿): 设 n 为位数(位宽)。总共有 2 n 整数可用 n 位表示。 2 n - 1 整数以 1 开头(第一个 1 是固定的,剩余的 n - 1 位可以是任何东西)。那些整数只需要循环的一次交互来确定MSB。此外,有 2 n - 2 整数从 01 开始,需要2次迭代, 2 n - 3 以 001 开头的整数,需要3次迭代,依此类推。
如果我们总结所有可能整数所需的迭代并将它们除以 2 n ,整数总数,我们得到所需的平均迭代次数确定 n 位整数的MSB:
(1 * 2 n - 1 + 2 * 2 n - 2 + 3 * 2 n - 3 +。 .. + n)/ 2 n
This series of average iterations is actually convergent and has a limit of 2 for n towards infinity
因此,天真的从左到右算法实际上对于任何数量的 O(1) 实际上是摊销的常数时间复杂度位。
答案 20 :(得分:1)
Woaw,这是很多答案。我很抱歉回答一个老问题。
int result = 0;//could be a char or int8_t instead
if(value){//this assumes the value is 64bit
if(0xFFFFFFFF00000000&value){ value>>=(1<<5); result|=(1<<5); }//if it is 32bit then remove this line
if(0x00000000FFFF0000&value){ value>>=(1<<4); result|=(1<<4); }//and remove the 32msb
if(0x000000000000FF00&value){ value>>=(1<<3); result|=(1<<3); }
if(0x00000000000000F0&value){ value>>=(1<<2); result|=(1<<2); }
if(0x000000000000000C&value){ value>>=(1<<1); result|=(1<<1); }
if(0x0000000000000002&value){ result|=(1<<0); }
}else{
result=-1;
}
这个答案与另一个答案很相似......哦。
答案 21 :(得分:1)
我谦虚的方法很简单:
MSB(x)= INT [Log(x)/ Log(2)]
转换:x的最高有效位是(基数x的对数除以基数2的对数)的整数值。
这可以轻松,快速地适应任何编程语言。在计算器上尝试一下,看看它是否有效。
答案 22 :(得分:0)
代码:
// x>=1;
unsigned func(unsigned x) {
double d = x ;
int p= (*reinterpret_cast<long long*>(&d) >> 52) - 1023;
printf( "The left-most non zero bit of %d is bit %d\n", x, p);
}
通过设置Y = 1
获取FPU指令FYL2X(Y * Log2 X)的整数部分答案 23 :(得分:0)
另一张海报使用字节宽度查找提供了查找表。如果您希望提高性能(以32K内存而不是仅256个查找条目为代价),这里是一个使用 15位查找表的解决方案,在 C#中7 表示 .NET 。
有趣的是初始化表格。由于它是我们在进程生命周期中所需的相对较小的块,因此我使用Marshal.AllocHGlobal
为此分配非托管内存。如您所见,为了获得最佳性能,整个示例都写为native:
readonly static byte[] msb_tab_15;
// Initialize a table of 32768 bytes with the bit position (counting from LSB=0)
// of the highest 'set' (non-zero) bit of its corresponding 16-bit index value.
// The table is compressed by half, so use (value >> 1) for indexing.
static MyStaticInit()
{
var p = new byte[0x8000];
for (byte n = 0; n < 16; n++)
for (int c = (1 << n) >> 1, i = 0; i < c; i++)
p[c + i] = n;
msb_tab_15 = p;
}
该表需要通过上面的代码进行一次性初始化。它是只读的,因此可以共享单个全局副本以进行并发访问。使用此表,您可以快速查找整数 log 2 ,这是我们在这里寻找的所有各种整数宽度(8,16, 32位和64位)。
请注意0
的表条目,这是唯一一个“最高设置位”概念的整数。未定义,给出值-1
。这种区别对于正确处理下面代码中的0值高位字是必要的。不用多说,这里是各种整数原语的代码:
ulong(64位)版本
/// <summary> Index of the highest set bit in 'v', or -1 for value '0' </summary>
public static int HighestOne(this ulong v)
{
if ((long)v <= 0)
return (int)((v >> 57) & 0x40) - 1; // handles cases v==0 and MSB==63
int j = /**/ (int)((0xFFFFFFFFU - v /****/) >> 58) & 0x20;
j |= /*****/ (int)((0x0000FFFFU - (v >> j)) >> 59) & 0x10;
return j + msb_tab_15[v >> (j + 1)];
}
uint(32位)版本
/// <summary> Index of the highest set bit in 'v', or -1 for value '0' </summary>
public static int HighestOne(uint v)
{
if ((int)v <= 0)
return (int)((v >> 26) & 0x20) - 1; // handles cases v==0 and MSB==31
int j = (int)((0x0000FFFFU - v) >> 27) & 0x10;
return j + msb_tab_15[v >> (j + 1)];
}
上述各种重载
public static int HighestOne(long v) => HighestOne((ulong)v);
public static int HighestOne(int v) => HighestOne((uint)v);
public static int HighestOne(ushort v) => msb_tab_15[v >> 1];
public static int HighestOne(short v) => msb_tab_15[(ushort)v >> 1];
public static int HighestOne(char ch) => msb_tab_15[ch >> 1];
public static int HighestOne(sbyte v) => msb_tab_15[(byte)v >> 1];
public static int HighestOne(byte v) => msb_tab_15[v >> 1];
这是一个完整的,有效的解决方案,代表了.NET 4.7.2上的最佳性能,可用于与专业性能测试工具进行比较的众多替代方案。其中一些在下面提到。测试参数是所有65位位置的均匀密度,即 0 ... 31/63 加上值0
(产生结果-1)。随机填充目标索引位置以下位。测试只有 x64 ,发布模式,启用了JIT优化。
这是我正式回答的结束;接下来是一些随意的注释以及与我运行的测试相关的替代测试候选源的源代码链接,以验证上述代码的性能和正确性。
上面提供的版本,编码为Tab16A,在许多运行中都是一致的赢家。这些不同的候选人可以找到here,here和here。
1 candidates.HighestOne_Tab16A 622,496 2 candidates.HighestOne_Tab16C 628,234 3 candidates.HighestOne_Tab8A 649,146 4 candidates.HighestOne_Tab8B 656,847 5 candidates.HighestOne_Tab16B 657,147 6 candidates.HighestOne_Tab16D 659,650 7 _highest_one_bit_UNMANAGED.HighestOne_U 702,900 8 de_Bruijn.IndexOfMSB 709,672 9 _old_2.HighestOne_Old2 715,810 10 _test_A.HighestOne8 757,188 11 _old_1.HighestOne_Old1 757,925 12 _test_A.HighestOne5 (unsafe) 760,387 13 _test_B.HighestOne8 (unsafe) 763,904 14 _test_A.HighestOne3 (unsafe) 766,433 15 _test_A.HighestOne1 (unsafe) 767,321 16 _test_A.HighestOne4 (unsafe) 771,702 17 _test_B.HighestOne2 (unsafe) 772,136 18 _test_B.HighestOne1 (unsafe) 772,527 19 _test_B.HighestOne3 (unsafe) 774,140 20 _test_A.HighestOne7 (unsafe) 774,581 21 _test_B.HighestOne7 (unsafe) 775,463 22 _test_A.HighestOne2 (unsafe) 776,865 23 candidates.HighestOne_NoTab 777,698 24 _test_B.HighestOne6 (unsafe) 779,481 25 _test_A.HighestOne6 (unsafe) 781,553 26 _test_B.HighestOne4 (unsafe) 785,504 27 _test_B.HighestOne5 (unsafe) 789,797 28 _test_A.HighestOne0 (unsafe) 809,566 29 _test_B.HighestOne0 (unsafe) 814,990 30 _highest_one_bit.HighestOne 824,345 30 _bitarray_ext.RtlFindMostSignificantBit 894,069 31 candidates.HighestOne_Naive 898,865
值得注意的是ntdll.dll!RtlFindMostSignificantBit
通过P / Invoke的可怕表现:
[DllImport("ntdll.dll"), SuppressUnmanagedCodeSecurity, SecuritySafeCritical]
public static extern int RtlFindMostSignificantBit(ulong ul);
这太糟糕了,因为这是整个实际功能:
RtlFindMostSignificantBit:
bsr rdx, rcx
mov eax,0FFFFFFFFh
movzx ecx, dl
cmovne eax,ecx
ret
我无法想象由于这五行而产生的糟糕表现,因此管理/原生过渡处罚必须受到指责。我也感到惊讶的是,测试确实支持在128字节(和256字节)short
(8位)上的32KB(和64KB)byte
(16位)直接查找表查找表。我认为以下内容与16位查找更具竞争力,但后者始终优于此:
public static int HighestOne_Tab8A(ulong v)
{
if ((long)v <= 0)
return (int)((v >> 57) & 64) - 1;
int j;
j = /**/ (int)((0xFFFFFFFFU - v) >> 58) & 32;
j += /**/ (int)((0x0000FFFFU - (v >> j)) >> 59) & 16;
j += /**/ (int)((0x000000FFU - (v >> j)) >> 60) & 8;
return j + msb_tab_8[v >> j];
}
我要指出的最后一点是,我很震惊,因为我的deBruijn方法并没有变得更好。这是我以前普遍使用的方法:
const ulong N_bsf64 = 0x07EDD5E59A4E28C2,
N_bsr64 = 0x03F79D71B4CB0A89;
readonly public static sbyte[]
bsf64 =
{
63, 0, 58, 1, 59, 47, 53, 2, 60, 39, 48, 27, 54, 33, 42, 3,
61, 51, 37, 40, 49, 18, 28, 20, 55, 30, 34, 11, 43, 14, 22, 4,
62, 57, 46, 52, 38, 26, 32, 41, 50, 36, 17, 19, 29, 10, 13, 21,
56, 45, 25, 31, 35, 16, 9, 12, 44, 24, 15, 8, 23, 7, 6, 5,
},
bsr64 =
{
0, 47, 1, 56, 48, 27, 2, 60, 57, 49, 41, 37, 28, 16, 3, 61,
54, 58, 35, 52, 50, 42, 21, 44, 38, 32, 29, 23, 17, 11, 4, 62,
46, 55, 26, 59, 40, 36, 15, 53, 34, 51, 20, 43, 31, 22, 10, 45,
25, 39, 14, 33, 19, 30, 9, 24, 13, 18, 8, 12, 7, 6, 5, 63,
};
public static int IndexOfLSB(ulong v) =>
v != 0 ? bsf64[((v & (ulong)-(long)v) * N_bsf64) >> 58] : -1;
public static int IndexOfMSB(ulong v)
{
if ((long)v <= 0)
return (int)((v >> 57) & 64) - 1;
v |= v >> 1; v |= v >> 2; v |= v >> 4; // does anybody know a better
v |= v >> 8; v |= v >> 16; v |= v >> 32; // way than these 12 ops?
return bsr64[(v * N_bsr64) >> 58];
}
有很多讨论deBruijn方法at this SO question的优越性和优秀性,我倾向于同意。我的推测是,虽然deBruijn和直接查找表方法(我发现最快)都必须进行表查找,并且两者都具有非常小的分支,但只有deBruijn具有64位乘法运算。我在这里只测试了IndexOfMSB
函数 - 而不是deBruijn IndexOfLSB
- 但我希望后者的机会要大得多,因为它的操作次数少得多(参见上文),而且我&#39;我可能会继续将它用于LSB。
答案 24 :(得分:0)
我假设您的问题是针对整数(在下面称为v)而不是无符号整数。
int v = 612635685; // whatever value you wish
unsigned int get_msb(int v)
{
int r = 31; // maximum number of iteration until integer has been totally left shifted out, considering that first bit is index 0. Also we could use (sizeof(int)) << 3 - 1 instead of 31 to make it work on any platform.
while (!(v & 0x8000000) && r--) { // mask of the highest bit
v <<= 1; // multiply integer by 2.
}
return r; // will even return -1 if no bit was set, allowing error catch
}
如果您想在不考虑标志的情况下使其工作,您可以添加额外的'v&lt;&lt; = 1;'在循环之前(并相应地将r值更改为30)。 如果我忘了什么,请告诉我。我没有测试过,但它应该可以正常工作。
答案 25 :(得分:0)
这是 C 的快速解决方案,可在 GCC 和 Clang 中使用;准备复制和粘贴。
#include <limits.h>
unsigned int fls(const unsigned int value)
{
return (unsigned int)1 << ((sizeof(unsigned int) * CHAR_BIT) - __builtin_clz(value) - 1);
}
unsigned long flsl(const unsigned long value)
{
return (unsigned long)1 << ((sizeof(unsigned long) * CHAR_BIT) - __builtin_clzl(value) - 1);
}
unsigned long long flsll(const unsigned long long value)
{
return (unsigned long long)1 << ((sizeof(unsigned long long) * CHAR_BIT) - __builtin_clzll(value) - 1);
}
和 C ++ 的改进版本。
#include <climits>
constexpr unsigned int fls(const unsigned int value)
{
return (unsigned int)1 << ((sizeof(unsigned int) * CHAR_BIT) - __builtin_clz(value) - 1);
}
constexpr unsigned long fls(const unsigned long value)
{
return (unsigned long)1 << ((sizeof(unsigned long) * CHAR_BIT) - __builtin_clzl(value) - 1);
}
constexpr unsigned long long fls(const unsigned long long value)
{
return (unsigned long long)1 << ((sizeof(unsigned long long) * CHAR_BIT) - __builtin_clzll(value) - 1);
}
代码假定value
不会是0
。如果要允许0,则需要对其进行修改。
答案 26 :(得分:0)
这看起来很大,但与循环相比,它的工作速度非常快,感谢 bluegsmith
int Bit_Find_MSB_Fast(int x2)
{
long x = x2 & 0x0FFFFFFFFl;
long num_even = x & 0xAAAAAAAA;
long num_odds = x & 0x55555555;
if (x == 0) return(0);
if (num_even > num_odds)
{
if ((num_even & 0xFFFF0000) != 0) // top 4
{
if ((num_even & 0xFF000000) != 0)
{
if ((num_even & 0xF0000000) != 0)
{
if ((num_even & 0x80000000) != 0) return(32);
else
return(30);
}
else
{
if ((num_even & 0x08000000) != 0) return(28);
else
return(26);
}
}
else
{
if ((num_even & 0x00F00000) != 0)
{
if ((num_even & 0x00800000) != 0) return(24);
else
return(22);
}
else
{
if ((num_even & 0x00080000) != 0) return(20);
else
return(18);
}
}
}
else
{
if ((num_even & 0x0000FF00) != 0)
{
if ((num_even & 0x0000F000) != 0)
{
if ((num_even & 0x00008000) != 0) return(16);
else
return(14);
}
else
{
if ((num_even & 0x00000800) != 0) return(12);
else
return(10);
}
}
else
{
if ((num_even & 0x000000F0) != 0)
{
if ((num_even & 0x00000080) != 0)return(8);
else
return(6);
}
else
{
if ((num_even & 0x00000008) != 0) return(4);
else
return(2);
}
}
}
}
else
{
if ((num_odds & 0xFFFF0000) != 0) // top 4
{
if ((num_odds & 0xFF000000) != 0)
{
if ((num_odds & 0xF0000000) != 0)
{
if ((num_odds & 0x40000000) != 0) return(31);
else
return(29);
}
else
{
if ((num_odds & 0x04000000) != 0) return(27);
else
return(25);
}
}
else
{
if ((num_odds & 0x00F00000) != 0)
{
if ((num_odds & 0x00400000) != 0) return(23);
else
return(21);
}
else
{
if ((num_odds & 0x00040000) != 0) return(19);
else
return(17);
}
}
}
else
{
if ((num_odds & 0x0000FF00) != 0)
{
if ((num_odds & 0x0000F000) != 0)
{
if ((num_odds & 0x00004000) != 0) return(15);
else
return(13);
}
else
{
if ((num_odds & 0x00000400) != 0) return(11);
else
return(9);
}
}
else
{
if ((num_odds & 0x000000F0) != 0)
{
if ((num_odds & 0x00000040) != 0)return(7);
else
return(5);
}
else
{
if ((num_odds & 0x00000004) != 0) return(3);
else
return(1);
}
}
}
}
}
答案 27 :(得分:-2)
使用 VPTEST(D, W, B) 和 PSRLDQ 指令的组合来关注包含最高有效位的字节,如下所示,使用 Perl 中这些指令的模拟,可在以下位置找到:
https://github.com/philiprbrenan/SimdAvx512
if (1) { #TpositionOfMostSignificantBitIn64
my @m = ( # Test strings
#B0 1 2 3 4 5 6 7
#b0123456701234567012345670123456701234567012345670123456701234567
'0000000000000000000000000000000000000000000000000000000000000000',
'0000000000000000000000000000000000000000000000000000000000000001',
'0000000000000000000000000000000000000000000000000000000000000010',
'0000000000000000000000000000000000000000000000000000000000000111',
'0000000000000000000000000000000000000000000000000000001010010000',
'0000000000000000000000000000000000001000000001100100001010010000',
'0000000000000000000001001000010000000000000001100100001010010000',
'0000000000000000100000000000000100000000000001100100001010010000',
'1000000000000000100000000000000100000000000001100100001010010000',
);
my @n = (0, 1, 2, 3, 10, 28, 43, 48, 64); # Expected positions of msb
sub positionOfMostSignificantBitIn64($) # Find the position of the most significant bit in a string of 64 bits starting from 1 for the least significant bit or return 0 if the input field is all zeros
{my ($s64) = @_; # String of 64 bits
my $N = 128; # 128 bit operations
my $f = 0; # Position of first bit set
my $x = '0'x$N; # Double Quad Word set to 0
my $s = substr $x.$s64, -$N; # 128 bit area needed
substr(VPTESTMD($s, $s), -2, 1) eq '1' ? ($s = PSRLDQ $s, 4) : ($f += 32); # Test 2 dwords
substr(VPTESTMW($s, $s), -2, 1) eq '1' ? ($s = PSRLDQ $s, 2) : ($f += 16); # Test 2 words
substr(VPTESTMB($s, $s), -2, 1) eq '1' ? ($s = PSRLDQ $s, 1) : ($f += 8); # Test 2 bytes
$s = substr($s, -8); # Last byte remaining
$s < $_ ? ++$f : last for # Search remaing byte
(qw(10000000 01000000 00100000 00010000
00001000 00000100 00000010 00000001));
64 - $f # Position of first bit set
}
ok $n[$_] eq positionOfMostSignificantBitIn64 $m[$_] for keys @m # Test
}