バックグラウンド
長い間、gcc は多くの組み込みのビット操作関数、特に末尾と先頭の 0 ビットの数を提供してきました (接尾辞andを持つlong unsigned
andについても同様です)。long long unsigned
l
ll
— 組み込み関数:
int __builtin_clz (unsigned int x)
x
最上位ビット位置から始まる、先頭の 0 ビットの数を返します。が 0 の場合x
、結果は未定義です。— 組み込み関数:
int __builtin_ctz (unsigned int x)
x
最下位ビット位置から開始して、の末尾の 0 ビットの数を返します。が 0 の場合x
、結果は未定義です。
ただし、テストしたすべてのオンライン (免責事項: x64 のみ) コンパイラでは、結果は両方とも、基になる組み込み型のビット数を返しますclz(0)
。ctz(0)
#include <iostream>
#include <limits>
int main()
{
// prints 32 32 32 on most systems
std::cout << std::numeric_limits<unsigned>::digits << " " << __builtin_ctz(0) << " " << __builtin_clz(0);
}
試行された回避策
モードの最新の Clang SVN トランクは、std=c++1y
これらすべての関数を緩和した C++14にしました。これにより、 、、およびの 3 /組み込みconstexpr
のラッパー関数テンプレートの SFINAE 式で使用する候補になります。ctz
clz
unsigned
unsigned long
unsigned long long
template<class T> // wrapper class specialized for u, ul, ull (not shown)
constexpr int ctznz(T x) { return wrapper_class_around_builtin_ctz<T>()(x); }
// overload for platforms where ctznz returns size of underlying type
template<class T>
constexpr auto ctz(T x)
-> typename std::enable_if<ctznz(0) == std::numeric_limits<T>::digits, int>::type
{ return ctznz(x); }
// overload for platforms where ctznz does something else
template<class T>
constexpr auto ctz(T x)
-> typename std::enable_if<ctznz(0) != std::numeric_limits<T>::digits, int>::type
{ return x ? ctznz(x) : std::numeric_limits<T>::digits; }
このハックの利点は、必要な結果を提供するプラットフォームが、ctz(0)
テストする余分な条件を省略できることですx==0
(これは、マイクロ最適化のように見えるかもしれませんが、組み込みのビット操作関数のレベルまで既に下がっている場合は、大きな違い)
質問
組み込み関数clz(0)
とのファミリはどの程度未定義ctz(0)
ですか?
std::invalid_argument
彼らは例外をスローできますか?- x64 の場合、現在の gcc ディストリビューションの場合、基になる型のサイズが返されますか?
- ARM/x86 プラットフォームは違いますか (それらをテストするためにアクセスできません)。
- 上記の SFINAE トリックは、そのようなプラットフォームを分離する明確な方法ですか?