Компактный шестнадцатеричный номер
Существует ли умный способ (т.е. безветристый) для "уплотнения" шестнадцатеричного числа. В основном переместите все 0s в одну сторону?
например:
0x10302040 -> 0x13240000
или
0x10302040 -> 0x00001324
Я смотрел бит Twiddling Hacks, но ничего не видел.
Это для алгоритма численного вращения SSE. Мне нужно удалить любые опорные точки, которые становятся 0. Я могу использовать _mm_cmpgt_ps
, чтобы найти хорошие опорные точки, _mm_movemask_ps
, чтобы преобразовать это в маску, а затем взломать хаки, чтобы получить что-то вроде выше. Значение шестнадцатеричного значения переходит в маску для команды _mm_shuffle_ps
для выполнения перестановки в регистре бит SSE 128.
Ответы
Ответ 1
Чтобы вычислить маску для _pext
:
mask = arg;
mask |= (mask << 1) & 0xAAAAAAAA | (mask >> 1) & 0x55555555;
mask |= (mask << 2) & 0xCCCCCCCC | (mask >> 2) & 0x33333333;
Сначала сделайте бит - или по парам бит, затем по квадратам. Маски предотвращают пересыщение смещенных значений на другие цифры.
После вычисления маски таким образом или способом harold (что, вероятно, быстрее) вам не нужна полная мощность _pext
, поэтому, если целевое оборудование не поддерживает его, вы можете заменить его следующим:
for(int i = 0; i < 7; i++) {
stay_mask = mask & (~mask - 1);
arg = arg & stay_mask | (arg >> 4) & ~stay_mask;
mask = stay_mask | (mask >> 4);
}
Каждая итерация перемещает все полубайты на одну цифру вправо, если имеется некоторое пространство. stay_mask
отмечает биты, которые находятся в их конечных положениях. Это использует несколько меньше операций, чем решение Hacker Delight, но все равно может выиграть от ветвления.
Ответ 2
Предположим, что мы можем использовать _pext_u32
, тогда проблема вычисляет маску с F для каждого нуля, которая не равна нулю. Я не уверен, что лучший подход, но вы можете вычислить OR из 4 бит кусания, а затем "разложить" его обратно на F следующим образом:
// calculate horizontal OR of every nibble
x |= x >> 1;
x |= x >> 2;
// clean up junk
x &= 0x11111111;
// spread
x *= 0xF;
Затем используйте это как маску _pext_u32
.
_pext_u32
может быть эмулирован этим (взят из Хакерского восторга, рис. 7.6)
unsigned compress(unsigned x, unsigned m) {
unsigned mk, mp, mv, t;
int i;
x = x & m; // Clear irrelevant bits.
mk = ~m << 1; // We will count 0 to right.
for (i = 0; i < 5; i++) {
mp = mk ^ (mk << 1); // Parallel prefix.
mp = mp ^ (mp << 2);
mp = mp ^ (mp << 4);
mp = mp ^ (mp << 8);
mp = mp ^ (mp << 16);
mv = mp & m; // Bits to move.
m = m ^ mv | (mv >> (1 << i)); // Compress m.
t = x & mv;
x = x ^ t | (t >> (1 << i)); // Compress x.
mk = mk & ~mp;
}
return x;
}
Но это немного катастрофа. Вероятно, лучше просто прибегнуть к разветвлению кода.
Ответ 3
uint32_t fun(uint32_t val) {
uint32_t retVal(0x00);
uint32_t sa(28);
for (int sb(28); sb >= 0; sb -= 4) {
if (val & (0x0F << sb)) {
retVal |= (0x0F << sb) << (sa - sb)
sa -= 4;
}
}
return retVal;
}
Я думаю, что это (или нечто подобное) - это то, что вы ищете. Исключение 0 кусков в пределах числа. Я не отлаживал его, и он работал только с одной стороны.
Ответ 4
Если ваш процессор поддерживает выполнение условных команд, вы можете получить выгоду от этого алгоритма:
uint32_t compact(uint32_t orig_value)
{
uint32_t mask = 0xF0000000u; // Mask for isolating a hex digit.
uint32_t new_value = 0u;
for (unsigned int i = 0; i < 8; ++i) // 8 hex digits
{
if (orig_value & mask == 0u)
{
orig_value = orig_value << 4; // Shift the original value by 1 digit
}
new_value |= orig_value & mask;
mask = mask >> 4; // next digit
}
return new_value;
}
Это выглядит как хороший кандидат для разворачивания цикла.
В алгоритме предполагается, что когда исходное значение смещено влево, нули сдвигаются, заполняя "пустые" биты.
Изменить 1:
На процессоре, который поддерживает условное выполнение инструкций, смещение исходного значения будет выполняться условно в зависимости от результата ANDing исходного значения и маски. Таким образом, нет ветвлений, только игнорируются инструкции.
Ответ 5
Я придумал следующее решение. Пожалуйста, взгляните, возможно, это поможет вам.
#include <iostream>
#include <sstream>
#include <algorithm>
using namespace std;
class IsZero
{
public:
bool operator ()(char c)
{
return '0' == c;
}
};
int main()
{
int a = 0x01020334; //IMPUT
ostringstream my_sstream;
my_sstream << hex << a;
string str = my_sstream.str();
int base_str_length = str.size();
cout << "Input hex: " << str << endl;
str.insert(remove_if(begin(str), end(str), IsZero()), count_if(begin(str), end(str), IsZero()), '0');
str.replace(begin(str) + base_str_length, end(str), "");
cout << "Processed hex: " << str << endl;
return 0;
}
Вывод:
Input hex: 1020334
Processed hex: 1233400