C++编译器识别2的幂吗
Does C++ compiler recognizes powers of 2?
我正在构建一个自定义哈希,其中我根据公式对字符串中的所有字母求和:
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具有逐位旋转指令(分别用于向右和向左旋转的ror
和rol
)。然而,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
生成的代码更好的系统上,尽管这台机器并没有硬件倍增。
如果您希望数字作为常量数组的初始值设定项当然,显而易见的答案是用其他方法生成它们程序,并插入生成的文本。
- 编译器无法识别在类声明之外定义的方法
- G++ 编译器无法识别 SQLAPI.h 头文件
- 编译器无法识别 Getline
- C ++中的函数为什么我的编译器无法识别错误()
- 编译器如何识别其主机的硬件?哪个组件?
- 当Mac Mojave中的C 编译时,C编译器识别是未知的
- 编译器无法识别我的结构成员
- 试图使用int []作为打字模板,编译器未识别函数最小的原型中的参数
- C++错误,编译器将无法识别字符串::p ush_back
- 为什么我的Visual Studio 2017编译器无法识别C++17类型推导指南
- 编译器未识别从类函数的呼叫作为课堂成员
- C++编译器识别2的幂吗
- Boost.Spirit编译器无法识别std::p air
- 编译器错误和字符串类无法识别方法
- 编译器未识别 getline()。此外,它不允许 CIN 和 COUT 使用字符串
- 使用 G++ -C 的 C++ 编译器错误:编译器将 const 字符串识别为 int
- 如何让编译器识别这些字符
- 如何强制编译器识别模板化函数中的lambda类型
- 如何让 MS C++编译器识别 #include 中未定义的类型
- C++ typedef 和返回类型:如何让编译器识别使用 typedef 创建的返回类型