C++编译器识别2的幂吗

Does C++ compiler recognizes powers of 2?

本文关键字:编译器 识别 C++      更新时间:2023-10-16

我正在构建一个自定义哈希,其中我根据公式对字符串中的所有字母求和:

string[0] * 65536 + string[1] * 32768 + string[2] * 16384 + ...

我遇到了一个问题,是否应该将这些数字定义为int数组中的常量,如下所示:

const int MULTIPLICATION[] = {
65536,
32768,
16384,
8192,
4096,
2048,
1024,
512,
256,
128,
64,
32,
16,
8,
4,
2,
1
}

或者,也许我应该在计算哈希本身的同时生成这些数字(同时可能由于尚未生成而失去一些速度)?我需要对这个散列进行数百万次计数,我想让编译器理解的主要事情是,与正常的MUL操作不同

MOV EBX, 8
MUL EBX

它会做

SHL EAX, 3

编译器是否理解,若我用2的幂来移位位,而不是通常的乘法?

另一个问题,我很确定当你用c写的时候,它确实会移位++数字*=2;但只是澄清一下,是吗?


谢谢,我发现了如何在调试器中查看不一致性。是的,如果你像一样使用它,编译器确实理解移位位

number *= 65536

然而,如果你进行,它会进行正常的乘法运算

number1 = 65536
number *= number1;

试试吧!

你在用什么编译器?您可以告诉大多数编译器在编译后保留中间文件,或者只编译(而不是汇编),这样您就可以实际查看它生成的汇编代码。

你可以在我的另一个问题上看到,这就是我所做的。

例如,在gcc中,-S标志的意思是"仅编译"。-masm=intel生成可读性更强的程序集IMO.


编辑

话虽如此,我认为以下是你正在寻找的算法(未经测试):

// Rotate right by n bits
#define ROR(a, n)   ((a >> n) | (a << (sizeof(a)*8-n)))

int custom_hash(const char* str, int len) {
int hash = 0;
int mult = 0x10000;  // 65536, but more obvious
for (int i=0; i<len; i++) {
hash += str[i] * mult;
mult = ROR(mult, 1);    
}
return mult;
}

首先,您没有指定当您有超过16个字符时会发生什么(乘数是什么?)所以在这个实现中,我使用了逐位旋转。x86具有逐位旋转指令(分别用于向右和向左旋转的rorrol)。然而,C没有提供表示旋转操作的方式。所以我定义了ROR宏,它为您进行旋转。(了解它的工作原理留给读者练习!)

在我的循环中,我像您一样以0x10000(65536)开始乘法器。循环的每次迭代,我都会将乘法器向右旋转一位。这基本上将它除以2,直到得到1,之后它变成0x80000000。

答案取决于编译器、硬件体系结构,以及可能的其他因素。

用移位代替这样的乘法是最佳做法,这甚至不是一个明显的先验。我认为通常应该让编译器进行指令级优化。

也就是说,让我们看看我的编译器是怎么做的:)

int i, j;
int main() {
j = i * 8;
}

这是使用gcc 4.7.2-O3编译的,导致

_main:
LFB0:
movq    _i@GOTPCREL(%rip), %rax
movl    (%rax), %edx
movq    _j@GOTPCREL(%rip), %rax
sall    $3, %edx                  ;<<<<<<<<<< THE SHIFT INSTRUCTION
movl    %edx, (%rax)
ret

因此,在我的环境中,答案显然是"是的"。

至于你的另一个问题,不要预先计算MULTIPLICATION。获取中的系数

string[0] * 65536 + string[1] * 32768 + string[2] * 16384 + ...

只需从coeff = 65536开始,每次迭代都向右移动一位:

coeff >>= 1;

为什么不直接使用C++中内置的移位运算符?

(string[0] << 16) + (string[1] << 15) + (string[2] << 14) + ...

您可以使用模板元编程,它确保在编译时计算2的幂,而不考虑编译器:

template<unsigned int SHIFT>
struct PowerOf2
{
static const size_t value = 1 << SHIFT;
};

为了便于使用,宏如下:

#define CONSTRUCT(I) (string[I] * PowerOf2<16 - I>::value)

现在使用,

CONSTRUCT(0)

相当于:

string[0] * 65536

您可以通过不断地乘以2来累加它。

int doubleRunningTotalAndAdd(int runningTotal, unsigned char c)
{
runningTotal *= 2;
runningTotal += c;
return runningTotal;
}
string s = "hello";
int total = accumulate(s.rbegin(), s.rend(), 0, doubleRunningTotalAndAdd);

没有规则;编译器将生成代码正确的结果。我知道的所有编译器都将使用当为最快的解决方案。我曾经研究过这样的系统整数乘法比移位快;我也工作过在编译器为h * 127生成的代码比为(h << 7) - h生成的代码更好的系统上,尽管这台机器并没有硬件倍增。

如果您希望数字作为常量数组的初始值设定项当然,显而易见的答案是用其他方法生成它们程序,并插入生成的文本。