Символы _bittest и _bittest64 являются внутренними компонентами компилятора, которые испускают инструкции битового теста , в частности x86 bt
, для проверки значения битас индексом, начинающимся с нуля.
С операндом памяти, bt
имеет поведение цепочки сумасшедших CISC, где битовый индекс может выходить за пределы dword / qword памяти, выбранного режимом адресации.Это медленно и поэтому компиляторы сначала загружают операнд в регистр.Но это то, что присуще MSVC.В противном случае он не должен быть встроенным.
Следующий C ++ соответствует поведению регистровой версии инструкции bt
, заключая счетчик сдвига в ширину регистра, то есть эффективно просматривая толькомладшие биты.( Это соответствует внутреннему MSVC , если b
<32 или <64 </em>. ). См. Обновленный код и комментарии для обсуждения того, как реализовать семантику MSVC, которая позволяет ему получать доступ извнеlong
или long long
.
Также следует помнить, что long
является 32-разрядным типом в x64 Windows ABI, но 64-разрядным типом в x86-64 System V ABI(который вы используете в OS X, если вы не создаете устаревший 32-битный код).Вы можете изменить свой код на int32_t
или uint32_t
, чтобы не оставлять неиспользуемые биты в каждом long
, в зависимости от того, как вы его используете.
inline
unsigned char bittest(long const *a, long b)
{
auto const value{ *a };
auto const mask{ 1L << (b&31) };
auto const masked_value{ value & mask };
return unsigned char{ masked_value != 0 };
}
inline
unsigned char bittest64(long long const *a, long long b)
{
auto const value{ *a };
auto const mask{ 1LL << (b&63) };
auto const masked_value{ value & mask };
return unsigned char{ masked_value != 0 };
}
Я не в курселюбые встроенные функции GCC или Clang с идентичной функциональностью.При необходимости вы можете вместо этого прибегнуть к генерации инструкций по сборке из реализаций функций, но bt
с операндом памяти медленен, поэтому обычно лучше всего реализовать его на чистом C ++ и позволить компилятору делать хорошую работу.
Обновление:
После обсуждения кода, испускаемого внутренними компонентами, стало ясно, что предложенный ранее код замены охватывает только часть функциональности.В частности, встроенные функции позволяют индексировать биты вне памяти, занимаемой *a
.Следующие реализации также учитывают это.
inline
unsigned char bittest(std::int32_t const *a, std::int32_t b)
{
auto const bits{ reinterpret_cast<unsigned char const*>(a) };
auto const value{ bits[b >> 3] };
auto const mask{ (unsigned char)(1 << (b & 7)) };
return (value & mask) != 0;
}
inline
unsigned char bittest64(std::int64_t const *a, std::int64_t b)
{
auto const bits{ reinterpret_cast<unsigned char const*>(a) };
auto const value{ bits[b >> 3] };
auto const mask{ (unsigned char)(1 << (b & 7)) };
return (value & mask) != 0;
}