在等式中:
在C语言中用给定的2的幂(a)求x的最快方法是什么?
编辑:
a到x的结果:
a | x
-------
1 | 0
2 | 1
4 | 2
8 | 3
16 | 4
32 | 5
64 | 6
128 | 7
256 | 8
512 | 9
...
选项1:脏循环
unsigned int get_power_of_two_exponent(unsigned int value)
{
unsigned int x = 0;
while( ( 1 << x ) != value)
{
x ++;
}
return x;
}
选项2:奇怪的把戏
#include <stdint.h>
#if defined(__GNUC__)
static int highest_bit_set(uint32_t value)
{
if (sizeof (unsigned int) == sizeof value)
return 31 - __builtin_clz(value);
else
if (sizeof (unsigned long) == sizeof value)
return 31 - __builtin_clzl(value);
else
exit(127); /* Weird architecture! */
}
#endif
有更快的选择吗?
答案 0 :(得分:4)
最快几乎总是查找表,以牺牲内存使用为代价。假定该值始终是2的幂,则可以创建一个查询表,如下所示:
uint8_t get_exponent (uint8_t val)
{
static const uint8_t byte[256] =
{
[1] = 0,
[2] = 1,
[4] = 2,
[8] = 3,
[16] = 4,
[32] = 5,
[64] = 6,
[128] = 7,
};
return byte[val & 0xFF];
}
如果您传递的值不是2的幂,它将返回0。
这可以通过循环遍历uint32_t的4个字节并执行4个表查找来进一步扩展。或者通过制作更大的查找表。
在x86上,我得到的内容可以归结为这个小巧的,无分支的机器代码:
get_exponent:
movzx edi, dil
movzx eax, BYTE PTR byte.2173[rdi]
ret
(在这种情况下,切换到uint_fast8_t
会得到相同的代码。)
答案 1 :(得分:2)
最快的方法,有点讽刺地 1 ,是写
switch (a)
{
case 1: return 0;
case 2: return 1;
case 4: return 2;
...
很显然,标签的类型与类型中的位一样多,但是仍然是O(1)。
您甚至可以使用成语a
将a ^ (a & (a - 1))
截断为2的幂,以可移植性为代价,因为仅当a
是2的补码类型时,才有效。
1 尽管在C ++中,您可以让编译器使用constexpr
和元编程技术来构建表。
答案 2 :(得分:2)
使用以下命令可获得最佳性能(在我的嵌入式ARM CORTEX M4 CPU内核上):
内置CLZ 解决方案(计数前导零)
此外,CLZ解决方案的内存效率远远高于第二名的查找表方法。
通常,LookUp表方法的效率仍然不如Builtin CLZ,因为该表存储在RAM中,例如DDR。因此,访问这种RAM中的数据可能需要花费十几个周期。在此示例中,这是由于启用了指令高速缓存而不启用了数据高速缓存而被放大的。此外,将这个巨大的表存储在缓存中不太合适。
答案 3 :(得分:0)
这取决于您要搜索多少值,以及是否定义了最大的输入值。
例如,如果x
可以是100
,则从步骤(x = 0)
开始x++
进行搜索,这不是很优雅且没有优化(100
检查)。您可以设置步骤x+=5
。如果结果低于搜索值,则x+=5
。如果更大,则退后x--
(最多4
次)。您可以根据需要调整步长。
如果存在“上限”,则可以创建一个可能的x
数组并实现二进制搜索。
答案 4 :(得分:0)
@Lundin的回答似乎在速度方面是最好的(仅3条汇编说明!),但是对于您的嵌入式系统而言,它可能不是一个好的选择。如果无法使用大型LUT:
我想,怪异的把戏似乎是一个快速的选择(不过,您应该对每个选项进行基准测试并查看实际结果)。您可以在存在的情况下使用它,否则可以回退到通常的移动方式:
..
..
stmt.executeUpdate(
"IF object_id('tempdb..#temp_table') IS NOT NULL DROP TABLE #temp_table");
stmt.execute("CREATE TABLE #temp_table([context] [varchar](100) NULL)");
stmt.execute("INSERT INTO #temp_table(context) VALUES ('" + context + "')");
如果要确保它是2的幂,可以使用popcnt。当输入不是2的幂时,while循环是一个无限循环,而我的循环只是根据最高位提供解决方案(根据您的需要,这可能是不正确的)。
答案 5 :(得分:0)
2 ^ x = a是等式
假设采用32位架构,并且“ a”和“ x”为整数。
这是我的方法
uint32_t x;
uint8_t *ptr ;
uint8_t ByteNo,BitNo,i;
void My_Function(uint32_t a)
{
ByteNo = BitNo = 9;//some random number
ptr = (uint8_t*)&a;//Assuming points to LSB in variable a
for(i=0;i<4;i++)
{
switch(*ptr)
{
case 0x01: BitNo=0;break;
case 0x02: BitNo=1;break;
case 0x04: BitNo=2;break;
case 0x08: BitNo=3;break;
case 0x10: BitNo=4;break;
case 0x20: BitNo=5;break;
case 0x40: BitNo=6;break;
case 0x80: BitNo=7;break;
case 0x00: BitNo=9;break;
default : break;//take care error condition
}
if(9 != BitNo)
{
break;
}
else
{
ptr++;
}
}//for loop
ByteNo = i;
x = (BitNo) + (ByteNo*8);
}//My_Function
另一种方法:
switch(a)
{
case 0x00000001: x=0; break;
case 0x00000002: x=1; break;
case 0x00000004: x=2; break;
case 0x00000008: x=3; break;
case 0x00000010: x=4; break;
case 0x00000020: x=5; break;
case 0x00000040: x=6; break;
case 0x00000080: x=7; break;
case 0x00000100: x=8; break;
case 0x00000200: x=9; break;
case 0x00000400: x=10; break;
case 0x00000800: x=11; break;
case 0x00001000: x=12; break;
case 0x00002000: x=13; break;
case 0x00004000: x=14; break;
case 0x00008000: x=15; break;
case 0x00010000: x=16; break;
case 0x00020000: x=17; break;
case 0x00040000: x=18; break;
case 0x00080000: x=19; break;
case 0x00100000: x=20; break;
case 0x00200000: x=21; break;
case 0x00400000: x=22; break;
case 0x00800000: x=23; break;
case 0x01000000: x=24; break;
case 0x02000000: x=25; break;
case 0x04000000: x=26; break;
case 0x08000000: x=27; break;
case 0x10000000: x=28; break;
case 0x20000000: x=29; break;
case 0x40000000: x=30; break;
case 0x80000000: x=31; break;
default: break;//error condition
}