Фон
В течение долгого времени gcc предоставлял ряд встроенных функций бит-скругления, в частности число конечных и ведущих 0-бит (также для long unsigned
и long long unsigned
, которые имеют суффиксы l
и ll
):
- Встроенная функция:
int __builtin_clz (unsigned int x)
Возвращает число ведущих 0-бит в
x
, начиная с самого значащего бита должность. Еслиx
равно 0, результат: undefined.- Встроенная функция:
int __builtin_ctz (unsigned int x)
Возвращает количество конечных 0-бит в
x
, начиная с младшего значащего бита должность. Еслиx
равно 0, результат: undefined.
В каждом онлайн-компиляторе (отказ от ответственности: только 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
сделал все эти функции расслабленными С++ 14 constexpr
, что делает их кандидатами для использования в выражении SFINAE для шаблона функции обертки вокруг 3 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
(что может показаться микро-оптимизацией, но когда вы уже достигли уровня встроенные функции бит-twiddling, это может иметь большое значение)
Вопросы
Как undefined - это семейство встроенных функций clz(0)
и ctz(0)
?
- могут ли они исключить
std::invalid_argument
? - для x64, будут ли они для текущего gcc-дистрибутива возвращать размер типа underyling?
- Существуют ли разные платформы ARM/x86 (у меня нет доступа к ним для тестирования)
- - это вышеописанный SFINAE трюк, четко определенный способ разделения таких платформ?