unsigned long long
やuint64_t
などの64bit整数では数値リテラルに接尾辞を忘れたためにビット演算を期待通りに行えないことがあります. ビット演算で謎の結果が出た時はこの辺を疑ってみるとよいでしょう.
例
指定した位置のビットを立てて返す関数set_bit
を考えます.
#include <bits/stdc++.h>
#include <stdint.h>
using namespace std;
// vのpos桁目のビットをたてる
uint64_t set_bit(uint64_t v, uint pos)
{
assert (1 <= pos && pos <= sizeof(uint64_t)*8);
return v | (1 << (pos-1));
}
int main()
{
uint is[] = {1,31,32,33,64};
for (uint i : is) {
uint64_t set = set_bit(0,i);
cout << "i=" << i << endl;
cout << set << endl;
}
return 0;
}
gcc5.4.0でコンパイルしたところ, これは次のような結果を返しました.
i=1
1
i=31
1073741824
i=32
18446744071562067968
i=33
1
i=64
18446744071562067968
この例の場合, 1
は最大で63ビットシフトされるのでそれだけのビット幅を持たなければいけません. したがってull
を付加してやれば修正できます.
@@ -7,7 +7,7 @@
uint64_t set_bit(uint64_t v, uint pos)
{
assert (1 <= pos && pos <= sizeof(uint64_t)*8);
- return v | (1 << (pos-1));
+ return v | (1ull << (pos-1));
}
i=1
1
i=31
1073741824
i=32
2147483648
i=33
4294967296
i=64
9223372036854775808
関連リンク
INT34-C. 負のビット数のシフトやオペランドのビット数以上のシフトを行わない
UndefinedBehaviorSanitizer — Clang 5 documentation